我是否正确使用导轨?

时间:2012-03-09 19:17:00

标签: ruby-on-rails ruby ruby-on-rails-3 refactoring

我觉得我不知道自己在做什么......我有一个模糊的想法。我希望到目前为止我能做到这一点。

任何你可以看到重构的方法都将非常感激。

有一件事我注意到它确实是错误的是它会加载以前提交的正确选项,如果有错误并且它发布到同一个URL。文本输入似乎加载了先前的值,但是select和单选按钮重置为每次提交的默认值。

ResourcesController#新

 def new
    @resource = Resource.new
    @title = "Submit Resource"
    @categories = Category.all
 end

ResourcesController #create(注意我在两个都有@categories = Category.all ...根据DRY我不确定它应该去哪里,或者它只适用于第一个表单提交。

  def create
    @title = "Submit Resource"
    @categories = Category.all

    @resource = Resource.new(params[:resource])

    category_ids = @categories.map { |c| c[1] }

    if @resource.valid? and category_ids.include? params[:category_id]
      @resource.cost = params[:cost]
      @resource.category_id = params[:category_id]
      @resource.save
      redirect_to root_url
    else
      render :action => :new
    end 
  end

Resource.rb(model)

# == Schema Information
#
# Table name: resources
#
#  id          :integer         not null, primary key
#  upvotes     :integer         default(0)
#  downvotes   :integer         default(0)
#  url         :string(255)
#  title       :string(255)
#  cost        :integer         default(0)
#  description :text
#  flags       :integer
#  category_id :integer
#  user_id     :integer
#  created_at  :datetime        not null
#  updated_at  :datetime        not null
#

class Resource < ActiveRecord::Base

  belongs_to :category
  belongs_to :user
  has_many :favorites
  has_many :resource_tags
  has_many :tags, :through => :resource_tags

  attr_accessible :url, :title, :cost, :description, :category_id, :user_id

  # Pseudo-Enum
  COST = [:free, :paid, :both]

  url_regex = /^(?:http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}(:[0-9]{1,5})?(\/.*)?$/ix

  validates :url,         :presence => true,
                          :format   => { :with => url_regex, 
                                         :message  => "must be valid"},
                          :uniqueness => { :case_sensitive => false,
                                           :message => "has already been submitted"}
  validates :title,       :presence => true,
                          :length   => { :within => 6..75 }
  validates :cost,        :presence => true
  validates :description, :presence => true,
                          :length   => { :within => 25..200 }
  validates :category_id, :presence => true,
                          :format   => { :with => /\d+/ }
  validates :user_id,     :presence => true,
                          :format   => { :with => /\d+/ }

  def cost
    COST[read_attribute(:cost)]
  end

  def cost=(value)
    write_attribute(:cost, COST.index(value.downcase.to_sym))
  end

  def category_id
    read_attribute(:category_id).to_i
  end

  def category_id=(value)
    write_attribute(:category_id, value.to_i)
  end

end

我的资源#新表单的视图文件

  <div class="field">
    <%= f.label :category %>
    <%= select_tag(:category_id, options_for_select(@categories.map {|c|[c.name, c.id]})) %> 
  </div>

最后一个问:我还没有使用过user_id字段。这将从设计中撤出,并将用户与提交的资源相关联。但是如何在不进行某种输入的情况下分配它,比如隐藏输入。这会在控制器的幕后进行吗?

3 个答案:

答案 0 :(得分:1)

看起来创建操作有问题

def create
    @title = "Submit Resource"
    @categories = Category.all

    @resource = Resource.new(params[:resource])
    if @categories.collect(&:id).include?(params[:category_id].to_i)
      @resource.category_id = params[:category_id]
    end
    @resource.user = current_user
    if @resource.valid?
      @resource.cost = params[:cost]
      @resource.save
      redirect_to root_url
    else
      render :action => :new
    end 
end

查看

<div class="field">
    <%= f.label :category %>
    <%= select_tag(:category_id, options_for_select(@categories.map {|c|[c.name, c.id]}, :selected => @resource.category_id)) %> 
</div>

答案 1 :(得分:1)

到你的上一个问题:

devise添加一个current_user方法,该方法是登录用户。因此,如果用户拥有多个资源,您可以执行以下操作:

@resource = current_user.resources.new(params[:resource])

第一个问题:

当表单呈现时,它是基于@resource&amp; @categories变量。当您发布表单时,将调用create action来创建新的@resource。如果保存因任何原因失败,则使用新的@resource变量重新呈现表单。您遇到的问题是,再次显示表单时未设置@ resource.category。所以你必须在is_valid之前这样做吗?检查。

 def create
    @title = "Submit Resource"
    @categories = Category.all

    @resource = Resource.new(params[:resource])
    @resource.category = Category.find(params[:category_id])

    if @resource.valid? # won't be valid if there is no category found.
      @resource.cost = params[:cost]
      @resource.save
      redirect_to root_url
    else
      render :action => :new
    end 
  end

但真正的问题在于你的形式。它应该将category_id嵌套在资源参数中,以便在执行Resource.new(params [:resource])时设置类别。

检查控制台中的POST请求正文或其他内容,看看它是否嵌套在资源中。我不知道它的确切语法,但如果你改变它,你可以删除@ resource.category = Category.find行。

答案 2 :(得分:1)

要搭载Sandip,你可以使用before_filter

来干掉你的行动
class ResourcesController < ApplicationController
  before_filter :load_categories, :only => [:show, :create]

  def new
    @resource = Resource.new
  end

  def create
    @resource = Resource.new(params[:resource])
    @resource.category = Category.find(params[:category_id])

    if @resource.valid? # won't be valid if there is no category found.
      @resource.cost = params[:cost]
      @resource.save
      redirect_to root_url
    else
      render :action => :new
    end 
  end

  private

  def load_categories
    @categories = Category.all
  end
end

如果您打算在应用程序布局中粘贴@title,我会在您的视图中将@title更改为:

yield(:title) || 'My Site'

并在适当的页面上使用:

content_for(:title) do
  Submit Resource

默认为“我的网站”。除非另有说明。