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

来源:互联网 发布:域名劫持检测 编辑:程序博客网 时间:2024/06/06 06:57


本章内容:

  • 会话和会话管理
  • 添加模型间的关系
  • 创建一个按钮,可添加产品到购物车中
迭代D1:寻找购物车
将购物车放在数据库中,并在会话中存储该购物车的唯一标识符,cart.id。每当请求出现时,可以从会话中找到该购物车的标识,并用该标识在数据库中查找购物车。
1、创建购物车
rails generate scaffold cart

2、应用迁移
rake db:migrate

3、修改控制器app/controllers/application_controller.rb
Rails的会话像是一个散列,将购物车对应的数据库id(cart.id)赋值给标识符为cart_id的session中。
[ruby] view plaincopy
  1. class ApplicationController < ActionController::Base  
  2.   protect_from_forgery  
  3.   
  4.   private  
  5.     def current_cart  
  6.       Cart.find(session[:cart_id])  
  7.     rescue ActiveRecord::RecordNotFound  
  8.      cart=Cart.create  
  9.      session[:cart_id]=cart.id  
  10.     cart  
  11.  end  
  12. end  
先从session对象中得到:cart_id,试图寻找与该id对应的购物车。如果没有找到,创建新的cart,并将新购物车的id保存在会话中。
迭代D2:将产品放到购物车中
1、创建在线商品表存放在线商品、购物车和产品之间的关系。
如图所示,在线商品line_items描述了购物车carts和商品products之间的关系。



创建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指令。
[ruby] view plaincopy
  1. class Product < ActiveRecord::Base  
  2.   attr_accessible :description:image_url:price:title  
  3.   validates :title:description:image_url:presence => true  
  4.   validates :price:numericality => {:greater_than_or_equal_to => 0.01}  
  5.   validates:title:uniqueness => true  
  6.   validates :image_url:format => {  
  7.   :with => %r{\.(gif|jpg|png)$}i,  
  8.   :message => 'must be a URL for GIF,JPG or PNG image.'  
  9. }  
  10.   default_scope :order => 'title'  
  11.   has_many :line_items  
  12.   before_destroy :ensure_not_referenced_by_any_line_itme  
  13.   
  14.  private  
  15.   def ensure_not_referenced_by_any_line_itme  
  16.    if line_items.empty?  
  17.     return true  
  18.    else  
  19.     errors.add(:base,'Line Items present')  
  20.     return false  
  21.   end  
  22.  end  
  23. end  

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

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

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

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

[ruby] view plaincopy
  1. <% if notice %>  
  2. <p id="notice"><%= notice %></p>  
  3. <% end %>  
  4.   
  5. <h1>Your Pragmatic Catalog</h1>  
  6.   
  7. <% @products.each do |product| %>  
  8.   <div class="entry">  
  9.     <%= image_tag(product.image_url) %>  
  10.     <h3><%= product.title %></h3>  
  11.     <%= sanitize(product.description) %>  
  12.     <div class="price_line">  
  13.       <span class="price"><%= number_to_currency(product.price) %></span>  
  14.       <%= button_to 'Add to Cart', line_items_path(:product_id => product) %>  
  15.     </div>  
  16.   </div>  
  17. <% end %>  
修改效果:

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

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

修改后的create方法如下:

[ruby] view plaincopy
  1. # POST /line_items  
  2.  # POST /line_items.xml  
  3.  def create  
  4.    @cart = current_cart  
  5.    product = Product.find(params[:product_id])  
  6.    @line_item = @cart.line_items.build(:product => product)  
  7.   
  8.    respond_to do |format|  
  9.      if @line_item.save  
  10.        format.html { redirect_to(@line_item.cart,  
  11.          :notice => 'Line item was successfully created.') }  
  12.        format.xml  { render :xml => @line_item,  
  13.          :status => :created:location => @line_item }  
  14.      else  
  15.        format.html { render :action => "new" }  
  16.        format.xml  { render :xml => @line_item.errors,  
  17.          :status => :unprocessable_entity }  
  18.      end  
  19.    end  
  20.  end  
如以上代码所示,这部分的操作可以总结为:

跳转界面模板:

[ruby] view plaincopy
  1. <h2>Your Pragmatic Cart</h2>  
  2. <ul>      
  3.   <% @cart.line_items.each do |item| %>  
  4.     <li><%= item.product.title %></li>  
  5.   <% end %>  
  6. </ul>  

3、结果测试

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


百度发现大家都遇到了这个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。

修改模型层文件

重新测试,运行正常:



0 0