Rails - How can I display one nested attributes - ruby-on-rails

I have a new problem, I Create a web where I upload many images, using nested attributes and polymorphic table, in my index.html I want to show only one image, but I can't find how. But I'm new in rails.
photography.rb
class Photography < ActiveRecord::Base
validates :title, :description, presence: true
belongs_to :user
has_many :images, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :images, :reject_if => lambda { |a| a[:img_str].blank? }, :allow_destroy => true
end
image.rb
class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
mount_uploader :img_str, AssetUploader
end
index.html.erb
<% for photo in #photo %>
<%= link_to photo.title, photography_path(photo) %>
<% photo.images.each do |images| %>
<%= images.img_str %>
<% end %>
<% end %>
With the for method I show all the image, try add .first, but says undefined method first for 5:Fixnum. I think that I have to create a helper method, but I not sure. Can anyone help me?. Thanks

Try:
<% for photo in #photo %>
<%= link_to photo.title, photography_path(photo) %>
<%= photo.images.first.img_str if photo.images.any? %>
<% end %>
Also, for is very rarely used in ruby, instead do:
<% #photos.each do |photo| %>

Related

Check box that creates nested objects in rails

I'm building an app that creates exams. For the part where a user selects the answers on the exam, I want to use a checkbox (or a radio button) to allow them to pick the answer.
I want all the user-selected answers to be a table in itself called "responses". I can't figure out how to use a radio button to create records.
All the response record needs to do is take the ID's of the Exam, User, and Score. Score is a table that tracks the user's scores and the number of correct answers.
Here's my examination model (rails wouldn't let me use the word "exam"). I have it set for nested attributes.
class Examination < ApplicationRecord
belongs_to :user
has_many :questions, dependent: :destroy
has_many :scores
has_many :responses
has_secure_password
accepts_nested_attributes_for :responses, allow_destroy: true
end
The response model is pretty basic:
class Response < ApplicationRecord
belongs_to :user
belongs_to :score
belongs_to :examination
end
Here's the "take an exam" page:
<%= link_to "Back to all exams", examinations_path %>
<h2><%= #exam.name %></h2>
<h3><%= #exam.intro %></h3>
<%= form_for #exam do |f| %>
<%= f.hidden_field :name, value: #exam.name %>
<%= fields_for :responses do |res_f| %>
<% #exam.questions.each_with_index do |question, i| %>
<% index = i + 1 %>
<h2>Question #<%=index%></h2><span style="font-size: 24px; font-weight: normal">(<%= question.points %> Points)</span>
<hr>
<h3><%= question.body %></h3>
<% question.answers.each do |ans| %>
<table>
<tr>
<td><%= res_f.check_box :answer_id , ans.id, :examination_id , #exam.id, :user_id %></td>
<td><%= ans.body %></td>
</tr>
</table>
<% end %>
<% end %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
This code doesn't run because Rails expects the responses records to exist in order to use the form. It throws this error:
undefined method `merge' for 484:Integer
If I tweak that checkbox code to this:
<%= res_f.check_box :answer_id %>
The code will run and it will give me the following params on submit:
Started PATCH "/examinations/34" for 127.0.0.1 at 2018-02-24 16:22:41 -0800
Processing by ExaminationsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"y4vcPByUKnDdM6NsWDhwxh8MxJLZU4TQo+/fUrmKYEfb3qLn5FVieJAYirNRaSl0w5hJax20w5Ycs/wz1bMEKw==", "examination"=>{"name"=>"Samuel Smith’s Oatmeal Stout"}, "responses"=>{"answer_id"=>"1"}, "commit"=>"Submit", "id"=>"34"}
I know it's not right but I was hoping it would create a record at least. All the checkbox has to do it create a response record. It should be able to grab the answer_id, exam_id and user_id. That's it.
Does anyone know how to do this?
Edit in response to Pablo 7:
Here are the other models (they're pretty basic right now)
class Score < ApplicationRecord
belongs_to :user
belongs_to :examination
has_many :responses, dependent: :destroy
end
class User < ApplicationRecord
has_many :examinations, dependent: :destroy
has_many :scores, dependent: :destroy
has_many :responses, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
class Question < ApplicationRecord
belongs_to :examination
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers, allow_destroy: true
validates_presence_of :body
validates_presence_of :question_type
end
#exam and Examination are the same. There is a "take" action in the Examination controller that allows a user to take an exam:
def take
#exam = Examination.find(params[:id])
#score = #exam.scores.build
#score.user_id = current_user.id
#score.save
end
So an exam belongs to the user that created it. The same user or a different one can take an exam using the take action. They would then have a score that belongs to them.
I think you must do some changes to your models:
A Response should belong to a Question (it's the question the user is responding).
A Response should belong to an Answer (it's the correct Answer for the question; the one that the user checks). If you want to allow multiple correct answers, this should be changed.
A Response should not belong to an Examination and should not belong to a User. In fact, a Response belongs to a Score and that's enough because the Score already belongs to an Examination and to a User.
An Examination should not have many responses. In fact, an Examination has many scores and scores have many responses. If you want, you can use has_many :responses, through: :scores
A User should not have many Responses. They have many Scores and Scores have many Responses. If you want, you can use has_many :responses, through: :scores
When you create a new score (in take), you should create empty responses for each question in the examination:
def take
#exam = Examination.find(params[:id])
#score = #exam.scores.build(user_id: current_user.id)
#exam.questions.each { |question| #score.responses.build(question_id: question.id) }
#I don't think you should save here.
#This method is like the new method
#You should save when the score is submitted
##score.save
end
In your form:
I would change the form to the score model (not examination). If you are using nested routes it could be [#exam, #score]
This may have many errors, as I cannot test it right now. I hope the idea is clear:
<%= form_for #score do |f| %>
<%= f.hidden_field :name, value: #score.examination.name %>
<% #score.responses.each_with_index do |response, i| %>
<%= f.fields_for response do |res_f| %>
<% index = i + 1 %>
<h2>Question #<%= index %></h2>
<span style="font-size: 24px; font-weight: normal">
(<%= response.question.points %> Points)
</span>
<hr>
<h3><%= response.question.body %></h3>
<%= res_f.collection_radio_buttons :answer_id, response.question.answers, :id, :body %>
<% end %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
The submit should call a method in Score model to create a Score (ScoresController.create)
Thanks Pablo, I finally got it working. Your code didn't quite work but it put me on the right path. I changed the model associations around as you suggested. That does make more sense.
Here are my models:
class Answer < ApplicationRecord
belongs_to :question
has_many :responses, dependent: :destroy
end
class Examination < ApplicationRecord
belongs_to :user
has_many :questions, dependent: :destroy
has_many :answers, :through => :questions
has_many :scores
has_secure_password
end
class Question < ApplicationRecord
belongs_to :examination
has_many :answers, dependent: :destroy
has_many :responses
accepts_nested_attributes_for :answers, allow_destroy: true, :reject_if => :all_blank
validates_presence_of :body
validates_presence_of :question_type
end
class Response < ApplicationRecord
belongs_to :score
belongs_to :answer
belongs_to :question
end
class Score < ApplicationRecord
belongs_to :user
belongs_to :examination
has_many :responses, dependent: :destroy
accepts_nested_attributes_for :responses, allow_destroy: true, reject_if: :no_answer_id?
private
def no_answer_id?(att)
att['answer_id'].blank?
end
end
I had to add that special method to the Score model to account for unchecked responses. Otherwise, it would throw an error.
I moved the "take a test" logic and view to the Score controller. With your code, I was getting a double loop (questions listed multiple times). I learned that you can actually access the responses through the "form_for" form loop using "res_f.object". That's pretty cool.
I also had to add a hidden field on the radio button collection form to get the question id.
Here it is:
<%= link_to "Back to all exams", examinations_path %><br/>
<h2><%= #exam.name %></h2>
<h3><%= #exam.intro %></h3>
<%= form_for [#exam, #score] do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.fields_for :responses do |res_f| %>
<h2>Question # <%= res_f.object.question.position %></h2>
<span style="font-size: 24px; font-weight: normal">
(<%= res_f.object.question.points %> Points)
</span>
<hr>
<h3><%= res_f.object.question.body %></h3>
<p><%= res_f.collection_radio_buttons :answer_id, res_f.object.question.answers, :id, :body do |b| %></p>
<div>
<%= b.radio_button %>
<%= b.label %>
<%= res_f.hidden_field :question_id, value: res_f.object.question.id %>
</div>
<% end %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
And the Scores controller:
class ScoresController < ApplicationController
def new
#exam = Examination.find(params[:examination_id])
#score = #exam.scores.build(user_id: current_user.id)
#exam.questions.each do |question|
res = #score.responses.build(question_id: question.id)
logger.info question.id
logger.info res
end
end
def create
#exam = Examination.find(params[:examination_id])
#score = #exam.scores.build(score_params)
if #score.save
redirect_to examination_path(#exam)
else
logger.info #score.errors.full_messages
redirect_to root_path
end
end
protected
def score_params
params.require(:score).permit(:examination_id, :user_id,
responses_attributes: [:id, :answer_id, :question_id, :selected])
end
end
This all works fine if there is only one correct answer. I'll have to modify it later to account for multiple answers. At least it works! I'll give you the credit Pablo.
Cheers

Nested checkboxes in Rails

I'm trying to create an event app where each event has multiple tables and each table has multiple people sitting at a table the event has multiple tickets which map the people to the tables that they are sitting at -> in order to achieve this I have created a checkbox nested in the fields_for :tables (which is in turn in the event form) I presume something is wrong with either the strong parameters or the form itself but I have not been able to find any information that provides a solution to the problem.After checking the checkboxes in the form indicating which people are going to be sitting at this table and submitting the form and returning to the form I find that the checkboxes are no longer checked???
here are the contents of my model files
# models
class Event < ActiveRecord::Base
has_many :tables, dependent: :destroy
has_many :people , through: :tickets
has_many :tickets
accepts_nested_attributes_for :tickets, allow_destroy: true
accepts_nested_attributes_for :tables, allow_destroy: true
end
class Table < ActiveRecord::Base
belongs_to :event
has_many :tickets
has_many :people, through: :tickets
end
class Ticket < ActiveRecord::Base
belongs_to :table
belongs_to :person
end
class Person < ActiveRecord::Base
has_many :tickets
has_many :tables, through: :tickets
end
Here is the form with parts omitted for brevity.
<%= form_for(#event) do |f| %>
...
<%= f.fields_for :tables do |builder| %>
<%= render 'table_field', f: builder %>
<% end %>
<%= link_to_add_fields "Add Table", f, :tables %>
...
<% end %>
And here is the checkbox list I have implemented within the table_field.
<% Person.all.each do |person| %>
<div class="field">
<%= check_box_tag "table[people_ids][]", person.id, f.object.people.include?(person) %> <%= f.label [person.first_name, person.last_name].join(" ") %>
</div>
<% end %>
this is the event_params
def event_params
params.require(:event).permit(:name, :description, :start, :end, :latitude, :longitude, :address, :data, :people_ids => [], tables_attributes: [:id, :number, :size, :people_ids => []]).tap do |whitelisted|
whitelisted[:data] = params[:event][:data]
end
How do I get the checkboxes to be persistently checked in this form?
You can use http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes
<%= f.collection_check_boxes(:people_ids, Person.all, :id, :name) do |person| %>
<%= person.label { person.check_box } %>
<% end %>
It will persist data as well.

How to customize fields_for

I'm a rails newbie, today I got a problem with fields_for. Hope anyone can help me.
I have a model project:
class Project < ActiveRecord::Base
validates :project_name, presence: true,uniqueness: true
validates :plan_time, presence: true
has_many :tasks, dependent: :destroy
accepts_nested_attributes_for :tasks, allow_destroy: true
end
and a model task:
class Task < ActiveRecord::Base
belongs_to :user
belongs_to :project
validates :user_id, presence: true
validates :project_id, presence:true
end
but when I made a form_for project:
<%= form_for(#project, do |f| %>
<%= f.fields_for :tasks do |tasks_for_form|%>
<%= render 'task_fields', f: tasks_for_form%>
<%end%>
...
it render all the existing task of the project in db. plz help me!
That's what fields_for does, what are you expecting it to do?
If you want only fields for a new task, then you need to pass in a record object to the builder, for example:
<%= f.fields_for :tasks, #project.tasks.new do |tasks_for_form| %>
If you want add/remove functionality, you need to add that yourself, check out the cocoon gem which makes it simple to do so.
I'm not sure you've got end inside partial, but the end is required when fields_for has initiated.
<%= form_for(#project, do |f| %>
<%= f.fields_for :tasks do |tasks_for_form|%>
<%= render 'task_fields', f: tasks_for_form%>
<% end %>
<% end %>

Rails ActiveRecord_Relation

I am currently developing a somewhat 'big' project. In this project I have many models, views, and controllers from which I have to mention the following:
Group.rb:
class Group < ActiveRecord::Base
has_many :users, through: :grouprel
has_many :grouprel, dependent: :destroy
validates :name, presence: true, length: {maximum: 25},
uniqueness: { case_sensitive: false }
validates :description, presence: true , length: {maximum: 140}
end
Grouprel.rb
class Grouprel < ActiveRecord::Base
belongs_to :user
belongs_to :group
validates :user_id, presence: true
validates :group_id, presence: true
end
User.rb
class User < ActiveRecord::Base
.....
has_many :groups, through: :grouprel, dependent: :destroy
has_many :grouprel, dependent: :destroy
.....
StaticPageController:
class StaticPagesController < ApplicationController
def home
if logged_in?
#tweet = current_user.tweets.build
#feed_items = current_user.feed.paginate(page: params[:page])
#groupi = Grouprel.where(user_id: current_user.id).pluck(:group_id)
#groupies = Group.where(id: #groupi).paginate(page: params[:page])
end
end
.....
end
Home.html.erb:
<% if logged_in? %>
.......
<section class="user_info">
<%= render 'shared/group_participation' %>
</section>
</aside>
.............
_group_participation.html.erb
<h5> Your groups: </h5>
<% if #groupies %>
<ol class="tweets">
<%= content_tag_for(:li, #groupies) do %>
<%= link_to #groupies.name, group_path(#groupies) %>
<% end %>
</ol>
<%= will_paginate #groupies %>
<% end %>
here I want to display every single group that a user is part of. The error I get when trying to get the #groupies in the StaticPagesController is %23<Group::ActiveRecord_Relation:0x007f86b00f6ed0> . I checked in my rails console , and it should return something.
What my limited knowledge about rails and ruby can tell is that this is a problem because the StaticPageController can't see the Grouprel.rb table. I tried to include controllers in herlpers. I even tried to define a method that returns 'groupies' in the application controller and then use that in the StaticPagesController. Could I get a hint of why I get that error returned ?
If my post has to contain any more specifications please do tell I will post them the second I see the request
You're not iterating over the groupies collection and are calling the name method on the collection itself. content_tag_for can iterate over the collection for you but you need to use the value it yields to the block:
<%= content_tag_for(:li, #groupies) do |group| %>
<%= link_to group.name, group_path(group) %>
<% end %>

Rails 4 - How can I show all nested attributes?

I'm new in Rails, I do a model with nested attributes, works perfectly. But in the time to show all nested attributes, I get an error.
Model
class Slide < ActiveRecord::Base
belongs_to :user
has_many :images, :dependent => :destroy
accepts_nested_attributes_for :images, :reject_if => lambda { |a| a[:img_str].blank? }
end
class Image < ActiveRecord::Base
belongs_to :slide
validates :img_str, presence: true
mount_uploader :img_str, AssetUploader
end
slides_controller
def index
#slide = Slide.all
end
slide/view/index
<% #slide.each do |slide| %>
<%= slide.images.img_str %>
<% end %>
Rails show this:
undefined method `img_str' for #Image::ActiveRecord_Associations_CollectionProxy:0x00000003e98dd0
How I can make the the association with image model?
images is a collection of image objects. What you are doing is like calling the attribute of image object on an array of image objects
<% #slide.each do |slide| %>
<%slide.images.each do |image|%>
<%= image.img_str %>
<%end%>
<% end %>

Resources