开发者

Rails associations and forms

I have a model called appointment. Each appointment has a stylist. In the form where I create a new appointment, I'm doing this:

  <div class="field">
    <%= f.label :stylist_id %><br />
    <%= f.select(:stylist_id, Stylist.order("name").map { |s| [s.name, s.id] }) %>
  </div>

This works but it's going to be tedious to do this kind of thing for every association in my app. I imagine Rails has some way of automatically generating select fields for associations but I have no idea how it works. Does such a thing exist?

Oh, and by the way, I already know about scaffolding. If scaffolding is supposed to take care of what I 开发者_C百科describe above, I'm apparently doing something wrong, because it's not doing that for me.

(I'm on Rails 3.)


Hmm, it seems that collection_select would work for the typical scenario:

<%= f.collection_select :stylist_id, Stylist.all, :id, :name %>


As much as RobinBrouwer's answer is correct, I just wanted to share a little nugget I came up with for one of our applications at work:

# config/initializers/to_options.rb
module ActiveRecord
  class Base
    def self.to_options(title=:name)
      self.all.map{|r| [r.send(title), r.id]}
    end
  end
end

Now, you can use to_options on any model, with the flexibility of picking any field for the option text (which defaults to name).

Model.to_options # => Creates select options with [m.name, m.id]
AnotherModel.to_options(:title) # => Creates select options with [m.title, m.id]

Shouldn't be hard to modify with ordering if necessary, either.


This answer improves performance on @vonconrad 's answer taking advantage of Rail's optimized .pluck vs .map by orders of magnitude especially if used on models with high number of records, though you likely would not used the entire options model method in a view for example.

module ActiveRecord
  class Base
    def self.to_options(title=:name)
      self.pluck(title.to_sym, :id)
    end
  end
end

Here are the benchmark results on a data model with nearly 300k records with the first version using .all.map and with the improved .pluck way.

 15.560000   2.950000  18.510000 ( 19.059607)
  0.390000   0.020000   0.410000 (  0.539866)

=> [
    [0] #<Benchmark::Tms:0x00007fb6091fa628 @label="all", @real=15.474819000111893, @cstime=0.0, @cutime=0.0, @stime=0.5800000000000001, @utime=14.480000000000004, @total=15.060000000000004>,
    [1] #<Benchmark::Tms:0x00007fb5f6a52978 @label="pluck", @real=0.8451100001111627, @cstime=0.0, @cutime=0.0, @stime=0.10000000000000009, @utime=0.5999999999999943, @total=0.6999999999999944>
]


As far as I know Rails doesn't have an easier way to create select tags. You could use a gem like formtastic (https://github.com/justinfrench/formtastic) or simple_form (https://github.com/plataformatec/simple_form), which make creating forms way easier. I prefer simple_form myself, so I'd advice you to try that out.

Another way is creating your own select helper that automatically gets the associated records from the database. You could also put the Stylist.order... stuff inside the Model:

# Model
def self.select
  Stylist.order("name").map { |s| [s.name, s.id] }
end

# Form
<%= f.select(:stylist_id, Stylist.select) %>

Makes your view look a bit nicer. :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜