I've built a lookup table in my Rails application.
It is a table that stores lookup text values for drop-down pickers or possibly check boxes. I want the lookup table to have a view associated so the values can be easily edited. I also may want to share lookup values among multiple models for a single field in the future.
So far I've managed to get it to work for the selection of values, but then displaying the text value again on a show or index view has been problematic.
This is how I built the lookup table
rails g scaffold Lookup field_name lookup_text table_name note
In the edit.html.erb where there is a lookup on a field, I've got code like this, which works and allows me to pick from a list.
<div class="field">
<%= f.label :status %><br />
<%= f.collection_select :status, Lookup.find(:all,:conditions => ["table_name = 'course' and field_name = 'status'"]), :id, :lookup_text, include_blank: true,:prompt => "Status" %>
</div>
That all works fine. When I try to display it back I cannot find the correct syntax. The best I have found is this:
(in the controller)
#status = Lookup.where(:id => #course.status).pluck(:lookup_text)
(in the view)
<p>
<b>Status:</b>
<%= #status %>
</p>
I think I am getting the entire object. It displays like this:
Status: ["Active"]
My questions are:
(1) How do I display the value only?
(2) Is this the best approach?
I've had a look at these and other SO questions, but none are really what I am looking for:
Rails Polymorphic with Lookup Table
Implementing a lookup table in Rails
EDIT
OK this works, but it doesn't look like it is the correct solution. Has anyone got a better way of doing this?
#status = Lookup.where(:id => #course.status).pluck(:lookup_text)[0]
Just another way to show the value is #status = Lookup.find(#course.status).lookup_text
Why not to try use classes for different lookups:
class CourseStatus < ActiveRecord::Base
set_table_name "lookups"
default_scope where("table_name = 'course' and field_name = 'status'")
end
class Course
belongs_to :course_status
end
You then can use:
CourseStatus.all # e.g. to fill select options
Course.first.course_status.lookup_text # => "Active" or smth else
Or without classes:
class Lookup
def self._by_table_and_field(table, field)
['table_name = ? and field_name = ?', table, field]
end
scope :by_table_and_field, lambda { |table, field|
where(Lookup._by_table_and_field(table, field))
}
end
class Course
belongs_to :status, class_name: 'Lookup', conditions: Lookup._by_table_and_field('course', 'status')
end
Lookup.by_table_and_field('course', 'status').all
Course.first.status.lookup_text
Related
I have two models, Chasing and User, a chasing belongs_to :user and a user has_many :chasings.
I created a migration for linking the two models together:
class AddUsersToChasings < ActiveRecord::Migration
def change
add_reference :chasings, :user, index: true, foreign_key: true
end
end
I have a controller for creating new users which I then want to be able to assign to chasings. I currently have this code in my chasings form for selecting the user:
<%= f.select :user_id, options_for_select(User.all.map {|c| [c.name, c.id]}), { :include_blank => "Please select user"}, {:class => "form-control"} %>
This seems to do the trick, after calling Chasing.first in rails console I can see the chasing now has user_id relevant to the user I picked. I can also run Chasing.first.user.name to give me the name of the user who is associated with the chasing. I'm wanting to show this name in my index view, the code I currently have for this is:
ChasingsController:
def index
#chasing = Chasing.all
end
Index view:
<% #chasing.each do |chasing| %>
<%= chasing.user %>
<% end %>
This shows a random string (seems to change every time I update a chasing - #<User:0xf5b0ba8> for example). when I change this to chasing.user.name I get 'undefined method `name' for nil:NilClass'.
Is there a way I can call the name for my view?
EDIT:
As per NickM's comment below I had chasings without users assigned to them causing active record to throw the error.
Looks like you have some Chasing objects in your database without user_ids. You can test by doing <%= chasing.user.name if chasing.user %>
I am trying to create the for attribute for a label in a nested form (Using nested_form). Is there a way to get the id of a corresponding f.checkbox?
HAML:
= label_tag '???', "Set as On", class: primary_btn
= f.check_box :is_on
Additional Information:
The current Model structure is like Post has many Images with field is_on
So I would like to create a nested field group like:
<label class="primary_btn" for="post_images_attributes_0_is_on">Set as primary</label>
<input id="post_images_attributes_0_is_on" name="post[images_attributes][0][is_on]" style="display:none;" type="checkbox" value="1">
The trick is to use fields_for. It gives you a "scoped" form builder instance which creates inputs for the nested fields.
= form_for (:post) do |f|
# ... a bunch of fields
= f.fields_for :images do |builder|
= builder.label :is_on, "Set as primary"
= builder.check_box :is_on
However your solution has some real gotchas:
Every time you change the primary image you need to update all the post.images to ensure that only one image has the is_on flag.
You need to do primary_image = post.images.where(is_on: true)
It won't work if the image can belong to many posts.
A better solution is create a special relation to the primary image on Post.
class Post < ActiveRecord::Base
has_many :images
has_one :primary_image, class_name: 'Image'
end
This would store the primary image as an integer in posts.primary_image_id instead of as a boolean flag on images.
We can use collection_select to get select tag to display the primary image attribute.
= form_for (#post) do |f|
# ... a bunch of fields
= f.fields_for :images do |builder|
# ...
= f.collection_select(:primary_image, #post.images, :id, :name)
This admittedly is not really optimal from a user experience perspective. A solution which requires javascript would be to have a hidden field for :primary_image and update its value when the user clicks the checkbox. If you are unsure how to do this please create a new question since its out of the scope of your original question.
I have checked some tutorials but I got confused by the parameters in this method
collection_select (object, attribute, collection, value_method, text_method, options = {}, html_options ={})
I have a map model includes: :area, :system, :file
and I want to read :area from database to a drop down list, and let user choose one
I already did #map = Map.all in the view
what the method should be?
especially the parameter "attribute". In a lot tutorials, people put "id" here. But I don't know what "id" is, and in my situation I don't need any other value, just the "area".
Im not exactly sure what you are asking here but if you are trying to make a dropdown selection for use in an html form will this example help you at all?
<% nations = {'United States of America' => 'USA', 'Canada' => 'Canada', 'Mexico' => 'Mexico', 'United Kingdom'=> 'UK'} %>
<% list = nations.sort %>
<%= f.select :country, list, %>
Here nations is a hash of countries then list becomes the sorted copy of that hash. An html dropdown is then created as a part of the form "f". ":country" is the part of the model that the data is connected to while list is the options to populate the dropdown with
It's not clear from your question what the model is that's being populated with the area.
Typically, collection_select is used between related models.
eg.
class Category < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :category
end
When selecting the 'category' for a product, your view would have something like:
<%= f.collection_select(:category_id, :id, Category.all, :name, include_blank: true) %>
What this does is specify the Product.category_id as the attribute being populated with the value of Category.id. The values come from the Category.all collection, and with Category.name being the item displayed in the select. The last (optional) parameter says to include a blank entry.
Something like the following is probably what you need:
<%= f.collection_select(:map_id, :id, #map, :area) %>
However, if the model you're trying to populate has an area attribute (instead of an ID linking to the map), you might need to use:
<%= f.collection_select(:area, :area, #map, :area) %>
This specifies that the area attribute of the receiving table will be populated with Map's area attribute, which is also being used as the "description" in the select.
I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.
This should be a layup for someone...
I'm trying to change a form field's attribute depending on which controller/model is calling the partial containing the form fields...
The issue (below) is with parent_id... which references one of two columns in a dogs table. It needs to either be kennel_id or master_id depending on which view this partial is being rendered in.
Not comfortable enough, yet, with Ruby/Rails language/syntax/tools to dynamically change this without getting bogged down in if/else statements.
I'm calling a shared partial and passing in a local variable:
= render "dogs/form", :parent => #kennel
or
= render "dogs/form", :parent => #master
In the partial I'd like to:
= form_for ([parent, target.dogs.build]) do |f|
= render "shared/error_messages", :target => parent
.field
= f.label :name
= f.text_field :name
.field
= f.hidden_field :parent_id ### <= PROBLEM
.actions
= f.submit 'Save'
Just thinking out loud:
I don't know if the parent-models have the proper names for it, but you could do something like:
= f.hidden_field "#{parent.class.name.underscore}_id"
But that doesn't look right. So, why not pass it as an argument?
= render "dogs/form", :parent => #master, :foreign_key => :master_id
Or, create aliases on the dog model to handle some sort of dynamic delegation:
class Dog
def parent_id=(parent_id)
case parent.class
when Master then self.master_id = parent_id
when Kennel then self.kennel_id = parent_id
end
end
def parent_id
case parent.class
when Master then self.master_id
when Kennel then self.kennel_id
end
end
end
But that sucks too. Could the relation be polymorphic? Then you can leave out the switching.
class Dog
belongs_to :owner, :polymorphic => true
end
= f.hidden_field :owner_id
Just some ideas. Hopefully one of them makes sense to you...
Wow, my initial answer wasn't even close. I think what you'll want is a polymorphic association: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations This way, the parent can be whatever class you need it to be.