填充" has_many"与未保存对象的关联(Order has_many LineItems)

时间:2014-06-10 18:34:40

标签: ruby-on-rails activerecord ruby-on-rails-4

我正在开发一个示例项目,允许创建订单,由许多产品组成,通过LineItems关联(请参阅我最近在Should I include other fields in a HABTM table?提出的问题)。

一旦我对新对象的视图被渲染,我就可以访问@orders对象了,看起来我应该像往常一样收集@line_items。问题是,@ orders对象尚未保存。

到目前为止,我在网上找到的最好的例子是a Railscasts writeup,它似乎鼓励在会话中存储对象参数:

def new
   session[:order_params] ||= {}
   @order = Order.new(session[:order_params])
   @order.current_step = session[:order_step]
 end

当我处理每个订单存储多个产品(通过LineItem)时,我只是不确定如何最好地支付这个费用。我想到的一件事是在数据库中创建并保存对象,但只有在用户实际保存它时才将其标记为“真实”(而不仅仅是在订单中添加项目) - 但是给出了放弃率等等,似乎我可能会得到太多的垃圾。

是否有一个被广泛接受的惯例来接受未保存的新订单并“正确地”构建一个has_many产品列表?为了记录,我试图使用RoR复制project I built in PHP,如果这有助于提供我的最终游戏的上下文。

我的架构(旨在支持为多个属性订购礼品卡)看起来像:

# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.    

ActiveRecord::Schema.define(version: 3) do    

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"    

  create_table "line_items", id: false, force: true do |t|
    t.integer "order_id"
    t.integer "product_id"
    t.decimal "pay_cost",      precision: 8, scale: 2,               null: false
    t.decimal "pay_discount",  precision: 8, scale: 2, default: 0.0
    t.decimal "pay_linetotal", precision: 8, scale: 2,               null: false
    t.text    "note"
  end    

  add_index "line_items", ["order_id", "product_id"], name: "index_line_items_on_order_id_and_product_id", unique: true, using: :btree    

  create_table "orders", force: true do |t|
    t.string   "order_id",                                              null: false
    t.integer  "property_id"
    t.string   "order_status",                          default: "new"
    t.string   "email_address"
    t.string   "bill_name"
    t.string   "bill_address1"
    t.string   "bill_address2"
    t.string   "bill_city"
    t.string   "bill_state"
    t.string   "bill_zip"
    t.string   "ship_name"
    t.string   "ship_address1"
    t.string   "ship_address2"
    t.string   "ship_city"
    t.string   "ship_state"
    t.string   "ship_zip"
    t.string   "pay_cardtype"
    t.string   "pay_pastfour"
    t.text     "order_summary"
    t.boolean  "is_gift",                               default: false
    t.text     "order_message"
    t.boolean  "pay_live",                              default: false
    t.boolean  "pay_paid",                              default: false
    t.boolean  "pay_refunded",                          default: false
    t.decimal  "pay_total",     precision: 8, scale: 2,                 null: false
    t.decimal  "pay_discount",  precision: 8, scale: 2, default: 0.0
    t.integer  "stripe_fee"
    t.string   "stripe_token"
    t.datetime "created_at"
    t.datetime "updated_at"
  end    

  add_index "orders", ["order_id"], name: "index_orders_on_order_id", unique: true, using: :btree
  add_index "orders", ["order_status"], name: "index_orders_on_order_status", using: :btree    

  create_table "products", force: true do |t|
    t.string  "name",                                          null: false
    t.decimal "price",  precision: 8, scale: 2,                null: false
    t.boolean "active",                         default: true
  end    

  create_table "products_properties", id: false, force: true do |t|
    t.integer "product_id"
    t.integer "property_id"
  end    

  add_index "products_properties", ["product_id", "property_id"], name: "index_products_properties_on_product_id_and_property_id", unique: true, using: :btree    

  create_table "properties", force: true do |t|
    t.string  "name",                   null: false
    t.string  "slug",                   null: false
    t.string  "prefix",                 null: false
    t.string  "phone"
    t.text    "address"
    t.string  "email"
    t.boolean "visible", default: true
  end    

  add_index "properties", ["prefix"], name: "index_properties_on_prefix", unique: true, using: :btree
  add_index "properties", ["slug"], name: "index_properties_on_slug", unique: true, using: :btree    

  create_table "properties_users", id: false, force: true do |t|
    t.integer "user_id"
    t.integer "property_id"
  end    

  add_index "properties_users", ["user_id", "property_id"], name: "index_properties_users_on_user_id_and_property_id", unique: true, using: :btree    

  create_table "users", force: true do |t|
    t.string   "first_name"
    t.string   "last_name"
    t.string   "email",                              null: false
    t.string   "encrypted_password", default: "",    null: false
    t.datetime "locked_at"
    t.boolean  "active",             default: true,  null: false
    t.boolean  "superuser",          default: false, null: false
  end    

  add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree    

end

2 个答案:

答案 0 :(得分:1)

将它存储在会话中应该没问题。如果您已配置为使用CookieStore进行会话,请尝试尽可能减少数据(理想情况下只是产品ID)。如果您有多步结帐流程,您可能还想查看wicked之类的内容。

我鼓励您考虑直接在数据库中存储订单。这可以让你做一些很酷的事情,比如当他们回到你的网站时向用户展示他们的废弃购物车,或者向在流程中间离开的人发送提醒电子邮件/特别优惠。

您还需要一个预定的清理流程来清除早于几周/几个月的不完整订单。

答案 1 :(得分:1)

要在请求之间保留购物车,您可以在会话中存储必要的详细信息,也可以使用实际的数据库记录,并将订单标记为临时/未确认。 在使用数据库的简单场景中,可能会过度,但您肯定希望避免在会话中使用复杂数据。如果你将来改变一些东西,它可能会破坏旧的会话。

我正在开发一个简单的网上商店,所以我将展示我如何使用会话来存储和检索工作订单。我在[{1}}中存储一个哈希,其中键是产品ID,值是数量

添加到购物车操作

session[:cart]

def add_to_cart product = Product.find(params[:product_id]) session[:cart][product.id] ||= 0 session[:cart][product.id] += 1 redirect_to :back end 方法在请求之间恢复before_action

@order

订单型号

class ApplicationController < ActionController::Base
  before_action :restore_order

  private

  def restore_order
    session[:cart] ||= {}
    @order = Order.build_from_session(session[:cart])
  end
end