Agile Web Development with Rails第九章笔记——任务D:创建购物车

本章内容:

  • 会话和会话管理
  • 添加模型间的关系
  • 创建一个按钮,可添加产品到购物车中
迭代D1:寻找购物车
将购物车放在数据库中,并在会话中存储该购物车的唯一标识符,cart.id。每当请求出现时,可以从会话中找到该购物车的标识,并用该标识在数据库中查找购物车。
1、创建购物车
rails generate scaffold cart
Agile Web Development with Rails第九章笔记——任务D:创建购物车_第1张图片
2、应用迁移
rake db:migrate

3、修改控制器app/controllers/application_controller.rb
Rails的会话像是一个散列,将购物车对应的数据库id(cart.id)赋值给标识符为cart_id的session中。
class ApplicationController < ActionController::Base
  protect_from_forgery

  private
    def current_cart
      Cart.find(session[:cart_id])
    rescue ActiveRecord::RecordNotFound
     cart=Cart.create
     session[:cart_id]=cart.id
    cart
 end
end
先从session对象中得到:cart_id,试图寻找与该id对应的购物车。如果没有找到,创建新的cart,并将新购物车的id保存在会话中。
迭代D2:将产品放到购物车中
1、创建在线商品表存放在线商品、购物车和产品之间的关系。
如图所示,在线商品line_items描述了购物车carts和商品products之间的关系。
Agile Web Development with Rails第九章笔记——任务D:创建购物车_第2张图片


创建Rails模型:
rails generate scaffold line_item product_id:integer cart_id:integer
应用迁移来创建相应的数据库表:
rake db:migrate
2、修改模型文件
在模型文件中添加一些声明来说明在线商品、购物车和产品之间的关系。(牵扯到cart、product和line_item的模型文件)
打开app/models下的文件cart.rb,添加一个对has_many的调用:

has_many :line_items一个购物车有许多相关联的在线商品。
:dependent => :destroy在线商品的存在依赖于购物车是否存在。
从相反的方向定义关系(从在线商品到carts和products表),修改app/models/line_item.rb

注:如果一个数据库表有外键,那么在相应模型中每个外键都要有个belongs_to声明。
belongs_to告诉Rails数据库,表line_items中的数据是依赖于表carts和表products的。
因为每个产品都可以有多个在线商品引用它,故需要在模型product中添加一个has_many指令。
class Product < ActiveRecord::Base
  attr_accessible :description, :image_url, :price, :title
  validates :title, :description, :image_url, :presence => true
  validates :price, :numericality => {:greater_than_or_equal_to => 0.01}
  validates:title, :uniqueness => true
  validates :image_url, :format => {
  :with => %r{\.(gif|jpg|png)$}i,
  :message => 'must be a URL for GIF,JPG or PNG image.'
}
  default_scope :order => 'title'
  has_many :line_items
  before_destroy :ensure_not_referenced_by_any_line_itme

 private
  def ensure_not_referenced_by_any_line_itme
   if line_items.empty?
    return true
   else
    errors.add(:base,'Line Items present')
    return false
  end
 end
end

上面的代码声明了一个产品有多个在线商品,并定义了一个hook(钩子)方法叫
ensure_not_referenced_by_any_line_itme。

hook方法就是在对象的生命周期中某个给定的地方Rails会自动调用的方法。

迭代D3:添加一个按钮
现在模型间的关系处理完毕,该给每个产品添加一个Add to Cart按钮了。

1、修改视图页面,使用line_items_path指定处理动作的控制器为在线产品控制器,向控制器传入欲加入购物车产品的id

<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>

<h1>Your Pragmatic Catalog</h1>

<% @products.each do |product| %>
  <div class="entry">
    <%= image_tag(product.image_url) %>
    <h3><%= product.title %></h3>
    <%= sanitize(product.description) %>
    <div class="price_line">
      <span class="price"><%= number_to_currency(product.price) %></span>
      <%= button_to 'Add to Cart', line_items_path(:product_id => product) %>
    </div>
  </div>
<% end %>
修改效果:

Agile Web Development with Rails第九章笔记——任务D:创建购物车_第3张图片

2、修改在线商品控制器的create方法。

将产品id传递给create方法,以便唯一的标识要添加的产品。

修改后的create方法如下:

 # POST /line_items
  # POST /line_items.xml
  def create
    @cart = current_cart
    product = Product.find(params[:product_id])
    @line_item = @cart.line_items.build(:product => product)

    respond_to do |format|
      if @line_item.save
        format.html { redirect_to(@line_item.cart,
          :notice => 'Line item was successfully created.') }
        format.xml  { render :xml => @line_item,
          :status => :created, :location => @line_item }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @line_item.errors,
          :status => :unprocessable_entity }
      end
    end
  end
如以上代码所示,这部分的操作可以总结为:

跳转界面模板:

<h2>Your Pragmatic Cart</h2>
<ul>    
  <% @cart.line_items.each do |item| %>
    <li><%= item.product.title %></li>
  <% end %>
</ul>

3、结果测试

完成上述步骤之后点击Add to Cart按钮,但是报错如下大哭

Agile Web Development with Rails第九章笔记——任务D:创建购物车_第4张图片

百度发现大家都遇到了这个bug,应该是版本差别所致。

由于写了“@cart.line_items.build(:product => product)”这句,这里要求必须把product属性放入attr_accessible中

原因在此
http://guides.rubyonrails.org/security.html#mass-assignment
为了安全考虑,如果允许mass-assignment的话,可以通过链接往数据库里插入数据
所以默认不可以直接用build来new这个新对象,除非加上
attr_accessible :product,:cart 允许mass-assignment。

修改模型层文件
Agile Web Development with Rails第九章笔记——任务D:创建购物车_第5张图片
重新测试,运行正常:
Agile Web Development with Rails第九章笔记——任务D:创建购物车_第6张图片


你可能感兴趣的:(Agile Web Development with Rails第九章笔记——任务D:创建购物车)