ruby-on-rails 如何迭代对象数组来创建类的新示例?Ruby on Rails

l3zydbqr  于 2023-01-10  发布在  Ruby
关注(0)|答案(2)|浏览(210)

我有一个订单模型,其中包含一个名为item_details的对象数组。
当我创建一个新订单时,我希望迭代item_details并创建包含订单id的OrderDetail的新示例。
OrderDetail是一个连接表,因此我希望在创建Order之后创建示例,以便在OrderDetails中包含order_id。
我该怎么做呢?我把item_details的数据类型设置为json,这样我就可以把它保存到我的数据库中。
之前我把它作为文本/字符串,它是保存一个符号作为字符串。
订购样品

{
    "id": 5,
    "customer_id": 1,
    "order_date": "2023-01-03",
    "total_cost": 0,
    "item_details": [
        {
            "product_id": 3,
            "quantity": 3
        },
        {
            "product_id": 9,
            "quantity": 4
        }
    ],

图式

create_table "order_details", force: :cascade do |t|
    t.integer "product_id"
    t.integer "order_id"
    t.integer "quantity"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "orders", force: :cascade do |t|
    t.integer "customer_id"
    t.string "order_date"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.json "item_details"
  end

型号

class Order < ApplicationRecord
    belongs_to :customer
    has_many :order_details
    has_many :products, through: :order_details

end

class OrderDetail < ApplicationRecord

    validates :quantity, numericality: { only_integer: true }

    belongs_to :order
    belongs_to :product  

end

串行器

class OrderSerializer < ActiveModel::Serializer
  attributes :id, :customer_id, :order_date, :total_cost, :item_details

  belongs_to :customer
  has_many :order_details
  has_many :products

  def total_cost
    cost = []
    self.object.order_details.each do |details|
      product = self.object.products.find {|product| product.id == details.product_id}
      cost << product.price * details.quantity
    end
    return cost.sum
  end

 class OrderDetailSerializer < ActiveModel::Serializer
  attributes :id, :product_id, :order_id, :quantity, :product

  belongs_to :order
  belongs_to :product
end

订单管理员

class OrdersController < ApplicationController
    wrap_parameters format: []
    skip_before_action :authorized, only: :create

    def index
        orders = Order.all
        if orders
        render json: orders
        else
            render json: {error: "Order Not Found" }, status: :not_found
        end
    end

    def show
        order = Order.find_by(id: params[:id])
        if order
            render json: order
        else
            render json: { error: "Order Not Found" }, status: :not_found
        end
    end

    def create
        order = Order.create(order_params)
        if order.valid?
            order.item_details.each do |i|
                OrderDetail.create(order_id: params[:id], product_id: i[:product_id], quantity: i[:quantity])
            end
            render json: order
        else
            render json: { errors: order.errors.full_messages }, status: :unprocessable_entity
        end
    end

    def update
        order = Order.find_by(id: params[:id])
        if order
            order.update(order_params)
            render json: order
        else
            render json: { error: "Order Not Found" }, status: :not_found
        end
    end

    def destroy
        order = Order.find_by(id: params[:id])
        if order
            order.destroy
            head :no_content
        else
            render json: {error: "Order Not Found"}, status: :not_found
        end
    end

    private

    def order_params
        params.permit(:customer_id, :order_date, item_details: [:product_id, :quantity] )
    end

end

订单详细信息控制器

class OrderDetailsController < ApplicationController

    skip_before_action :authorized, only: :create

    def index
        order_details = OrderDetail.all
        if order_details
        render json: order_details
        else
            render json: {error: "Not Found"}, status: :not_found
        end
    end

    def create
        order_detail = OrderDetail.create(order_details_params)
        if order_detail.valid?
            render json: order_detail
        else
            render json: { errors: order_detail.errors.full_messages }, status: :unprocessable_entity
        end
    end

    def update
        order_detail = OrderDetail.find_by(id: params[:id])
        if order_detail
            order_detail.update(order_details_params)
            render json: order_detail
        else
            render json: { error: "Not Found" }, status: :not_found
        end
    end

    private

    def order_details_params
        params.permit(:order_id, :product_id, :quantity)
    end

end
5f0d552i

5f0d552i1#

例如

order = Order.create(order_attribs)
order_items.each do |item_attribs|
  order.order_items.create(item_attribs)
end
x6492ojm

x6492ojm2#

在Rails中,通常在单个请求中创建多个记录的方法是将父记录accept nested attributes作为其子记录:

class Order
  has_many :order_details
  accepts_nested_attributes_for :order_details
  validates_associated :order_details
end

这将创建一个order_details_attributes= setter,它接受一个哈希数组作为输入,并初始化/创建嵌套记录。

class OrdersController
  # POST /orders
  def create
    @order = Order.new(order_params)
    if @order.save
      # either just respond with a location
      head :created, location: @order
      # or the entity as json
      render json: @order
    else
      render json: { errors: order_detail.errors.full_messages }, 
        status: :unprocessable_entity
    end
  end

  private 

  def order_params
    params.require(:order)
          .permit(:foo, :bar, order_details_attributes: [:product_id, :quantity]) 
  end
end

然而,这实际上只是在一个同步请求中管理多个资源的一种杂七杂八的东西,并不总是能带来最好的用户体验或好的代码。
如果你的用户正在向购物车添加商品,最好在他们添加第一个商品时立即保存订单,然后让客户端发送原子POST /orders/:order_id/order_details请求以向购物车添加商品-更新单个商品的数量将使用PATCH /orders/:order_id/order_details/:id完成。
问题中的控制器也有很多问题,如果你只是从一个支架重新开始,你会更好。

  • 使用find而不是find_by(id: ...)。如果找不到记录,它将引发NotFoundException,并以404状态响应进行响应。它将在不添加大量循环复杂性和重复的情况下中断方法。您不需要返回json: { error: "Not Found" }。这只是一个愚蠢的反模式。
  • if order_detail.valid?实际上并不保证记录会持久保存到数据库中。它只是说验证已通过。请检查.save.persisted?的返回值。
  • 您实际上并没有检查order_detail.update(order_details_params)是否成功。始终为无效的用户输入编写代码。

相关问题