带单控制器的STI

时间:2014-04-01 16:38:16

标签: ruby-on-rails sti

我有几个模型(用户,目标),我的目标模型有几个子类型(运动,瑜伽等)

用户可以有多个目标,但每种类型只有一个。

class User < ActiveRecord::Base
    has_many :goals, dependent: :destroy
    has_one :exercise
    has_one :water
    has_one :yoga

    def set_default_role
        self.role ||= :user
    end
end

class Goal < ActiveRecord::Base
    self.inheritance_column = :description

    belongs_to :user
    validates :user_id, presence: true
    validates :description, presence: true
end

其中目标的子类就是这样的

class Exercise < Goal
    belongs_to :user
end

我想在我的目标控制器中创建所有类型的目标,并将其设置为localhostL:3000 / waters / new将成为我的“水目标类型”创建页面。我无法正确设置它,因此描述会自动设置(我的STI列),因为我也想通过我的用户构建它(因此也设置了user_id)。

我的目标控制器目前看起来像这样......

def create
   @goal =  current_user.goals.build
   ???? code to set goal.description = subtype that the request is coming from

   respond_to do |format|
      if @goal.save
        format.html { redirect_to @goal, notice: 'Goal was successfully created.' }
        format.json { render action: 'show', status: :created, location: @goal }
      else
        format.html { render action: 'new' }
        format.json { render json: @goal.errors, status: :unprocessable_entity }
      end
    end
end

我对rails很新,所以有点困惑。还使用Rails 4.0

1 个答案:

答案 0 :(得分:1)

在这种情况下,您需要以某种方式将要创建的类型传达给控制器。

最简单的方法是在表单中包含一个隐藏字段,该字段具有您要创建的类型的值。

假设表单的块变量为f

<%= f.hidden_field :type, :value => "Excercise" %>

然后你可以建立一个这样的目标:

current_user.goals.build(type: params[:type])

您可以轻松地看到这可能非常危险,在使用用户提交的数据时应始终小心谨慎。

为了防范恶意用户,您可以在控制器中设置常量

AcceptedModels = ["Excercise", "Yoga", "Water"]

def accepted_type
  (AcceptedModels & params[:type]).first
end

如果您还想隐藏内部结构,可以使用哈希并发送任何标识符

AcceptedModels = {"0": "Excercise", "1": "Yoga", "2": "Water"}

def accepted_type
  AcceptedModels.fetch(params[:type], nil)
end

在任何一种情况下,您都可以像这样建立目标:

current_user.goals.build(type: accepted_type)

最大的缺点是,无论何时添加/删除更多目标,都必须保持AcceptedModels更新