Ruby on Rails - Show field from has_many table - ruby-on-rails

I've got two tables: Ships and Voyages. A ship can have many voyages, but a voyage may only have one ship.
In RoR the ship model has a has_many :voyages and the Voyage model is set to belongs_to: ship
I can display all of the fields from the ship table in the view without any problems using code similar to this:
<%= #ship_data.id %>
I'm now trying to show a piece of information from the voyage table in the view.
If I do:
<%= #ship_data.voyages %>
I'm able to pull up the ActiveRecord entry i.e.:
#<Voyage::ActiveRecord_Associations_CollectionProxy:0x00000006639a10>
If I append .to_json I can pull up a json file with all the data.
How would I go about displaying a specific field in my view, things I've tried include:
<%= #ship_data.voyages.id %>
and
<%= #ship_data.voyages, :id %>
But both error out on me. Note: While the relationship is one ship to many voyages - currently each ship only has one voyage.
I will admit to being a bit of a RoR novice!

#ship_data.voyages is an ActiveRecord relation. It's like an array of items. You can get an array of ids of every item:
<%= #ship_data.voyages.pluck(:id) %>
Or loop through the array:
<% #ship_data.voyages.each do |voyage| %>
<%= voyage.id %>
<% end %>
Or get only first voyage:
<%= #ship_data.voyages.first.id %>

Related

Edit/create nested resources in Formtastic (Rails)

This question is regarding Rails 4/postgresql and the app is hosted on Heroku.
I am making a Quiz-functionality on a website and I am wondering on how to implement the forms (using Formtastic) best to make this is easy as possible. I have three models:
Quiz (has_many :quiz_questions), e.g. "Test to see how awesome you are"
QuizQuestion(belongs_to :quiz, has_many :quiz_options). e.g. "1. Which is your favorite color")
QuizOption (belongs_to :quiz_question). e.g. "Blue"
I have set up the forms like this:
<%= semantic_form_for([:admin, #quiz], :url => admin_quiz_path(#quiz.id)) do |f| %>
<%= render 'form' , :f => f %>
<% end %>
where the form looks like this:
<%= f.inputs %>
<h3>Quiz questions</h3>
<%= f.semantic_fields_for :quiz_questions do |qq_f| %>
<%= qq_f.inputs %>
<h4>Quiz options</h4>
<%= qq_f.semantic_fields_for :quiz_options do |qqo_f| %>
<%= qqo_f.inputs %>
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit %>
or go <%= link_to 'back', admin_quizzes_path %>
<% end %>
It seems, however, not to be working the way I want. I expect to be able to see the fields of QuizQuestion and QuizOptions in this form (there are objects for those) but I don't.
More importantly is that I would like to be able to create a New QuizQuestion and subsequently QuizOption in this form. It doesn't necessarily have to be jQuery/ajax or anything but I would like to do it all from this form.
Basically, I would like my workflow to be like:
Create a Quiz and add values to it. Click Create.
Add QuizQuestion number one and add the values to it (like "name label"). Click Create.
Add QuizOption related to QuizQuestion number one, and its "name label". Click create.
Repeat for QuizQuestion/QuizOption until the Quiz is done.
How can I do this?
For your workflow you might have to add accept_nested_attributes_for for the nested resources, this way when creating an object object you can actually create nested children (as long as they fulfill all the validations). This way:
# A quiz :has_many :quiz_questions
#quiz = Quiz.create(...)
with a declaration like:
has_many :quiz_questions
accepts_nested_attributes_for :quiz_questions
in your Quiz model you'll actually be able to create QuizQuestion from the quiz model like:
# using the previously quiz model
quiz.quiz_questions.create(...)
Doing the same for the deeply nested associations will do have the same effect.
Perhaps the reason why you don't see any field on the form is because there is not nested object created. Let me explain. When you create a new Quiz object, in your quizs_controller (or whatever the inflection for quiz is...) you need a:
def new
quiz = Quiz.new()
end
and
def create
Quiz.new(quiz_params)
end
private
def quiz_params
# whitelisted parameters sent along with the form
params.require(:quiz).permit(...)
end
if you actually want to be able to see the fields in the form you'll have to use the build method and actually populate that new object with respective the nested resources.
Note that for this to work with the form you will have to whitelist in the quizzes_controller the right attributes. You can debug the params you receive once you send the new quiz formulary and check that everything is right.
TIP! if you don't want to worry about the JS when adding nested resources dynamically, I recommend you using the cocoon gem

Rails output polymorphic associations

I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.

Rails - showing comments in a different area

Im trying to create a system which allows athletes to respond to their coaches traing plan, to do this i have allowed the coach to create contentsm however i am using a blogging based system to create it... at the moment the page displays like so
CONTENT TITLE
Content info 1...
Content info 2...
Content info 3...
COMMENTS...
comment 1
comment 2
comment 3
.etc
However i want to set it so that there can only be 7 Comments Max per post as well as set out like this per post...
CONTENT TITLE
Content info 1...
comment 1
Content info 2...
comment 2
Content info 3...
comment 3
.etc
I realise this is probably not the best way to do want i want, but it works (just dosnt appear in the place i want it to)
I did do experiments with creating more models, but kept getting errors whenever i tryed to run more than 1 comment system per post. I was wondering if i could have some help in sorting this out, or any methods i could do to make this easier, or even better if the models would work and if i was just doing something wrong?? tell me if this isn't enough information to go off, and ill try provide some more! Thankyou
.
.
EDIT:
The models i have used are
Program - As in the training plan set for the week
Coaches - The coach that is inputing the data to the rider
Riders - To comment on the coaches data with their own data.
I am unsure what files are need exactly so i have included the link to the github page i am pushing to ( https://github.com/effectonedesign/coacheasy1 ), if there is any other info needed, please let me know!
I like what "mind" has said however, i have done everything have said, in my def show (program controller) it is saying there is an error and i keep getting this message undefined method `coaches' for nil:NilClass everything is identical to his but im getting issues, i really do appreciate the help! Thanks
I would probably create 3 models for the above, TrainingPlan, Section (or content, text_block etc.) and Comment.
Then do the following
TrainingPlan has_many :sections
Section belongs_to :training_plan
Section has_one :comment (if you allow only 1 comment per section, otherwise use has_many)
Comment belongs_to :section
Now, to achieve the formatting you wanted do the following in your views:
<% #training_plan.sections.each do |section| %>
<%= section.text %>
<%= section.comment.text %>
<% end %>
If you allow multiple comments:
<% #training_plan.sections.each do |section| %>
<%= section.text %>
<% section.comments.each do |comment| %>
<%= comment.text %>
<% end %>
<% end %>
Form for comments
I haven't tested the following, so you might need to tweak some parts.The training plan controller:
def show
# using includes will query the database 3 times only (once for each table) rather than
# querying it 1 + N + N (in this case 7 sections, 7 comments possibly, so 15 times)
#training_plan = TrainingPlan.includes(:sections, sections: :comment).find(params[:id])
#sections = #training_plan.sections
#sections.each do |section|
# only build a new comment if there is no comment for that section already
section.build_comment unless section.comment
end
end
In your view views/training_plans/show.html.erb
<%= #training_plan.title %> # or whatever
<% #sections.each do |section|
<%= #section.content %>
<% if section.comment %>
<%= section.comment.content %>
<% else %>
<%= render 'comments/form', comment: section.comment %> # or wherever you have the form
<% end %>
<% end %>
views/comments/_form.html.erb
# This might break if you have a separate comment action somewhere which passes an
# instance variable #comment to the form
<%= form_for comment do |f| %>
# normal form stuff
<% end %>
If that all works then on your training plan show page you should see each section, and if it has a comment then that comment will be rendered, otherwise a form will be shown.
Depending on your routes you might need to run rake routes and see where your comment create action is, and then pass that to the form <%= form for comment, url: some_url_helper_here do |comment| %>
If it was me I would create the add comment part through JavaScript, sort of like in this railscast, but since you're new to RoR I've tried to keep it simple.

Rails Form Builders - How to display a read only field or protect a field

I have created a form that needs to show data from 2 tables (parent and child). When the form is submitted only the child fields are updated (the parent fields are meant to be display only). While the parent model fields are displayed these need to be protected from updates (preferably via the formbuilder, rather than via css).
FWIW this is a pretty common master/detail use case. However I have not been able to find any examples of this - most of the examples I've seen seem to be trivial/single model display/update where all displayed fields are updateable).
Any ideas/samples/suggestion/tutorials/examples of real world, multi model Rails forms would be helpful.
TIA
Just out of interest, why bother going through the motions of creating a multi-model form when you only want to update the child record? My advice would be keep your form simple, I.e. make it a child form and just display the data from the parent record. If needs be, you could even style that display to look like part of the form, although I think that may throw the user off.
If you really need to do what you are doing, I would still use CSS to disable/readonly the input fields and in your controller update action, only pass the attributes you want to update into the update_attributes method call.
Finally, maybe look into the attr_protected method to prevent the fields you may want protecting from accidental mass-assignment.
I agree with tsdbrown, I don't think a complex form is required. If you'd like to learn more about complex forms or you really have your heart set on using a complex form I'd recommend watching the Railscasts episodes (73 - 75).
As tsdbrown said before, you are adding a complexity layer to your forms that's not need. If all you want is to update a detail model, while showing some info of it's parent, you could just do so with something like:
Order number: <%= #line_item.order.number %>
Order date: <%= #line_item.order.order_date %>
<% form_for #line_item do |f| %>
<%= f.label :quantity %>
<%= f.text_edit :quantity %>
<% end %>
When you'd like to edit both, then you can research on the field_for and nested forms methods (the Railscasts suggestion mentioned before it's great).
Thx for the responses which helped resolve my problem/question. Just want to close this out in case it helps others in the future.
Turns out I had been getting an error trying to reference my parent data element (patients.lname) as it was being passed in an array of results, rather than as a single result. In my view controller I had:
#visit = Visit.all(:select => "visits.id, visits.visittype, visits.visitdate, events.patient_id, patients.lname",
:conditions => ["visits.id = ?",params[:id] ],
:joins => "INNER JOIN events on events.id = visits.event_id INNER JOIN patients on patients.id = events.patient_id" )
In my view I had (this was giving me an invalid reference as I was doing a find all above):
<h1>Editing visit for patient :
<%= #visit.lname %> # should be <%= #visit[0].lname %>
</h1>
Below is the improved (and simpler) version where I find the specific record I need (basically replacing find all with find first):
#visit = Visit.find(:first, :select => "visits.id, visits.visittype, visits.visitdate, events.patient_id, patients.lname",
:conditions => ["visits.id = ?",params[:id] ],
:joins => "INNER JOIN events on events.id = visits.event_id INNER JOIN patients on patients.id = events.patient_id" )
And in the view:
<% form_for(#visit, :builder => LabelFormBuilder) do |f| %>
<%= f.error_messages %>
Name: <%= #visit.lname %>
<%= f.text_field :visittype %>
<%= f.date_select :visitdate %>
<p>
<%= f.submit 'Update' %>
</p>
<% end %>
Sometimes it's hard to see the wood for the trees! Hope this helps someone else.

How to list instances of a model and have a form to create an instance on the same view?

i have a model named 'chapter' (whose only attributes are 'name' and 'course__id') which belongs to "course" (and a course has_many chapters). on the course 'Show' view, I list all chapters for that course. Easy.
I want to add a form at the end of the list so that a user can easily create a new chapter.
so in my controller, I've added this:
#newchapter=#course.chapters.build
and the form on the view looks like this:
<% form_for([#course,#newchapter]) do |c| -%>
<%= c.label :name, "New Chapter" %>: <%= c.text_field :name %>
<%= c.submit 'Create' %>
<% end %>
(for the sake of clarity: it is outside of the #course.chapters.each block)
Now, the problem is that #course.chapters.size is the actual number of chapters + the empty one i created in the controller.
Is there a way to loop through all #course.chapters except the last (empty) one? or is there a better practice (i.e. not create #newchapter or not like this)?
thanks,
Pierre
You don't want to use #course.chapters.build here because this does add an empty chapter to the course. Instead you'll want to use Chapter.new and set the :course option like this.
#newchapter = Chapter.new(:course => #course)
It may not even be necessary to specify :course here depending on how you are using #newchapter.

Resources