I followed a tutorial to build a Restaurant reviews site on Ruby on Rails and i adapted it for Hospitals.
I would like to display results from the database in a sorted format like highest reviews or highest number of reviews or by location (all available data in the database) in a gridview (limited to Top 6).
I am unsure as to where to even start , I know it might sound a but simple but i have researched and keep seeing so many options and got confused as to where to start.
Some solutions seem to fit small data sets but i fully expect this to grow exponentially in the future so the most effective way would be best for me.
I have SQLlite on my local environment and Postgres in my production environment.
This is what i have on my search page that displays all my Hospitals and what i have in my index page which displays all records in a grid. Any assistance would be greatly appreciated.
search.html.erb
<div class="table-responsive table-box card">
<%= form_tag search_hospitals_path, method: :get, class: "form-group" do %>
<div class="input-group">
<div class="input-group-addon">Search and Review</div>
<p>
<%= text_field_tag :search, params[:search], class: "form-control formInput", placeholder: "Hospital Name" %>
</p>
</div>
<% end %>
<div class="container hospital_display">
<% #hospitals.each do |hospital| %>
<div class="row">
<div class="col-sm-6">
<%= link_to image_tag(hospital.image), hospital, class: "responsive" %>
</div>
<div class="col-sm-6">
<%= link_to hospital.name, hospital %><br>
<%= hospital.address %><br>
<%= hospital.phone %><br>
<%= link_to hospital.website, hospital.website %>
<%# Checks for admin %>
<% if user_signed_in? && current_user.admin? %>
<%= link_to 'Edit', edit_hospital_path(hospital), class: "btn btn-link" %>
<%= link_to 'Destroy', hospital, method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-link" %>
<% end%>
<%# Rating %>
<% if hospital.reviews.count == 0 %>
No reviews yet, be the first to write one
<% elsif hospital.reviews.count == 1 %>
<div class="star-rating" data-score= <%= hospital.avg_rating %> ></div>
1 Review
<% else %>
<div class="star-rating" data-score= <%= hospital.avg_rating %> ></div>
<%= hospital.length %> <%= "Reviews" %>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
</div>
</div>
<br>
<script>
$('.star-rating').raty({
path: 'https://s3-us-west-2.amazonaws.com/morafamedapp/stars',
readOnly: true,
score: function() {
return $(this).attr('data-score');
}
});
</script>
I have this on my index page
index.html.erb
<div class="jumboFluid">
<div class="jumbotron">
<section class="content">
<%= form_tag search_hospitals_path, method: :get, class: "form-group" do %>
<div class="input-group">
<div class="input-group-addon">Search</div>
<p>
<%= text_field_tag :search, params[:search], class: "form-control formInput", placeholder: "Eye, Maternity" %>
<%# <%= submit_tag "Search", name: nil, class: "btn btn-default" %>
</p>
</div>
<% end %><br>
<h3 class="intro2">Find the best HealthCare Options around you with Medapp.
<br><%# Explore the best of healthcare available in your community.<br>
Read and leave reviews to assist others seeking the best health care and keep our hospitals on their toes. %></h3>
</section>
</div>
</div>
<div class="hospitalList">
<h1 id="hospitalBanner">Hospitals</h1>
<blockquote>
<p class="text-center"><cite>“Explore the best of healthcare available in your community”</cite> </p>
</blockquote>
<% content_for(:body_attributes) do %>
data-no-turbolink="false"
<% end %>
<main>
<div class="container-fluid fluid">
<div class="row">
<% #hospitals.each do |hospital| %>
<div class="col-md-4 col-xs-6">
<div class="thumbnail">
<%= link_to image_tag(hospital.image), hospital %>
<div class="caption">
<h4> <%= link_to hospital.name, hospital %></h4><br>
<% if hospital.reviews.count == 0 %>
0 Reviews
<% elsif hospital.reviews.count == 1 %>
<div class="star-rating" data-score= <%= hospital.avg_rating %> ></div>
1 Review
<% else %>
<div class="star-rating" data-score= <%= hospital.avg_rating %> ></div>
<%= hospital.length %> <%= "Reviews" %>
<% end %>
</div>
</div>
</div>
<% end %>
</div>
</main>
<br>
<% if user_signed_in? && current_user.admin? %>
<%= link_to 'New Hospital', new_hospital_path, class: "btn btn-primary btn-lg btn-special" %>
<% end %>
<!--Start of Tawk.to Script-->
<script type="text/javascript">
window.$_Tawk = undefined;
var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
(function(){
var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
s1.async=true;
s1.src='https://embed.tawk.to/592da976b3d02e11ecc677a1/default';
s1.charset='UTF-8';
s1.setAttribute('crossorigin','*');
s0.parentNode.insertBefore(s1,s0);
s1.style.background = 'yellow';
})();
</script>
<!--End of Tawk.to Script-->
<script>
$('.star-rating').raty({
path: 'https://s3-us-west-2.amazonaws.com/morafamedapp/stars',
readOnly: true,
score: function() {
return $(this).attr('data-score');
}
});
</script>
</div>
Hospitals_controller.rb
class HospitalsController < ApplicationController
before_action :set_hospital, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:search, :index, :show]
before_action :check_user, except: [:search, :index, :show]
protect_from_forgery with: :null_session
protect_from_forgery except: ["create"]
def search
if params[:search].present?
#hospitals = Hospital.search(params[:search])
else
#hospitals = Hospital.all
end
end
def import
Hospital.import(params[:file])
end
# GET /hospitals
# GET /hospitals.json
def index
#hospitals = Hospital.all
end
# GET /hospitals/1
# GET /hospitals/1.json
def show
#reviews = Review.where(hospital_id: #hospital.id).order("created_at DESC")
if #reviews.blank?
#avg_rating = 0
else
#avg_rating = #reviews.average(:rating).round(2)
end
end
# GET /hospitals/new
def new
#hospital = Hospital.new
end
# GET /hospitals/1/edit
def edit
end
# POST /hospitals
# POST /hospitals.json
def create
import if params[:file] # <= this here is the call to your import method
#hospital = Hospital.new(hospital_params)
respond_to do |format|
if #hospital.save
format.html { redirect_to #hospital, notice: 'Hospital was successfully created.' }
format.json { render :show, status: :created, location: #hospital }
else
format.html { render :new }
format.json { render json: #hospital.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /hospitals/1
# PATCH/PUT /hospitals/1.json
def update
respond_to do |format|
if #hospital.update(hospital_params)
format.html { redirect_to #hospital, notice: 'Hospital was successfully updated.' }
format.json { render :show, status: :ok, location: #hospital }
else
format.html { render :edit }
format.json { render json: #hospital.errors, status: :unprocessable_entity }
end
end
end
# DELETE /hospitals/1
# DELETE /hospitals/1.json
def destroy
#hospital.destroy
respond_to do |format|
format.html { redirect_to hospitals_url, notice: 'Hospital was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_hospital
#hospital = Hospital.find(params[:id])
end
def check_user
unless current_user.admin?
redirect_to root_url, alert: "Sorry, only admins can do that!"
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def hospital_params
params.require(:hospital).permit(:name, :address, :city_town, :state, :phone, :website, :safe_care, :jci, :cohsasa, :best_known_4, :image )
end
end
In your Hospital model, you could just define scopes that you can append to any query:
# app/models/hospital.rb
class Hospital < ActiveModel
scope :order_by_highest_rated, -> { includes(:reviews).group("hospital_id").order("avg(reviews.rating) desc") }
scope :order_by_most_reviews, -> { includes(:reviews).group("hospital_id").order("count(reviews.id) desc") }
end
then in your Hospital controller, you can add functionality to choose the sort order
class HospitalsController < ApplicationController
def index
if params[:search_by] == 'highest_ranked'
#hospitals = Hospital.order_by_highest_rated
else
#hospitals = Hospital.order_by_most_reviews
end
end
end
See here and here for more readings
If you don't care about accessing the value itself and only want the order I suggest going with andrew21s answer. With that out of the way, let's get to my answer.
The question is simple enough although the answer is more complicated than you would expect. I assume the following associations in the models:
class Hospital < ApplicationRecord
has_many :reviews
end
class Review < ApplicationRecord
belongs_to :hospital
end
With the above present this issue can be solved with the following scopes:
class Hospital < ApplicationRecord
# ...
def self.include_review_count
review_count = Review.select(Review.arel_table[:id].count)
.where(Review.arel_table[:hospital_id].eq(arel_table[:id]))
.as('review_count')
select(*attribute_names.map(&:to_sym)).select(review_count)
end
def self.include_review_average
review_average = Review.select(Review.arel_table[:rating].average)
.where(Review.arel_table[:hospital_id].eq(arel_table[:id]))
.as('review_average')
select(*attribute_names.map(&:to_sym)).select(review_average)
end
# ...
end
With these scopes present you can now do the following in your controller:
class HospitalsController < ApplicationController
# ...
def index
#hospitals = Hospital.include_review_count
.include_review_average
.order('review_count DESC', 'review_average DESC', :id)
.limit(6)
end
# ...
end
Note: In the code above you can't use .order(review_count: :desc, review_average: :desc, id: :asc) because "review_count" is not a real attribute, but merely an alias. For this reason you'll have to use the literal (String) variant. The same is true when using this added attribute in a where statement. For example: .where(review_count: 2) doesn't work, whereas .where('review_count = ?', 2) does function.
This solution makes only one query and also allows you access the values without additional queries. An example could be (index.html.erb):
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Review Count</th>
<th>Review Average</th>
</tr>
</thead>
<tbody>
<% #hospitals.each do |hospital| %>
<tr>
<td><%= hospital.id %></td>
<td><%= hospital.name %></td>
<td><%= hospital.review_count %></td>
<td><%= hospital.review_average %></td>
</tr>
<% end %>
</tbody>
</table>
Related
Is it possible to set up a Rails form in a way that a user can upload images, and then be shown the direct link for their image before saving the rest of the form?
Use Case:
The use case is trying to be able to allow users to upload images, and then before saving the rest of the post, get links for those images and use the links in their markdown form in the same form.
I'm not familiar with Turbo, but maybe that's the way to go?
What I Have:
guide.rb
class Guide < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged
has_many_attached :images
acts_as_taggable_on :season, :tag
validates_presence_of :season_list, message: 'at least one season tag must be added'
validates_inclusion_of :season_list, in: %w[Spring Summer Fall Winter], message: '%<value>s is not a valid season'
end
guides_controller.rb
class GuidesController < ApplicationController
skip_before_action :verify_authenticity_token, only: %i[update_tags]
before_action :set_guide, only: %i[show edit update destroy]
rescue_from ActiveRecord::RecordNotFound, with: :guide_not_found
...
# GET /guides/new
def new
#guide = Guide.new
end
# GET /guides/1/edit
def edit; end
# POST /guides or /guides.json
def create
#guide = Guide.new(guide_params)
respond_to do |format|
if #guide.save
format.html { redirect_to guide_url(#guide), notice: 'Guide was successfully created.' }
format.json { render :show, status: :created, location: #guide }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #guide.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /guides/1 or /guides/1.json
def update
respond_to do |format|
if #guide.update(guide_params)
format.html { redirect_to guide_url(#guide), notice: 'Guide was successfully updated.' }
format.json { render :show, status: :ok, location: #guide }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #guide.errors, status: :unprocessable_entity }
end
end
end
...
private
# Only allow a list of trusted parameters through.
def guide_params
params.require(:guide).permit(:title, :body, :tag_list, :tags, season_list: [], images: [])
end
end
_form.html.erb
<%= form_with(model: guide, class: "contents", multipart: true) do |form| %>
<% if guide.errors.any? %>
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
<h2><%= pluralize(guide.errors.count, "error") %> prohibited this guide from being saved:</h2>
<ul>
<% guide.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="my-5">
<%= form.label :title %>
<%= form.text_field :title, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
</div>
<div class="my-5">
<%= form.label :season %>
<% %w(Spring Summer Fall Winter).each do |season| %>
<%= form.check_box :season_list, { multiple: true }, season, nil %>
<%= label_tag season %>
<% end %>
</div>
<div class="my-10" data-controller="tagify">
<%= form.hidden_field :tag_list, value: #guide.tag_list.to_s, data: {"tagify-target": "hiddenField"} %>
<%= form.label :tags %>
<div >
<div data-tagify-target="input"></div>
</div>
</div>
<div class="py-10">
<%= form.label :image %>
<%= form.file_field :image, multiple: true, direct_upload: true, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
</div>
<div class="my-5">
<%= form.label :body %>
<%= form.text_area :body, rows: 4, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
</div>
<div class="inline">
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
</div>
<% end %>
Any help or direction to research is appreciated.
I have an app that allows users to add albums they like to a database. Anyone can then come along and write a review for that album. Pretty simple.
I am running into a problem where an extra record appears to be created for the reviews on each album's show page. Every album, even if it has not ever been reviewed, has an additional, seemingly empty review being displayed when I use .each to display each review in the album show page. I want to get rid of that.
Here is an image of the problem. I have used CSS to highlight reviews in red. As you can see, there is an empty review at the bottom. When I inspect the review in question, it's raty title is "Not reviewed yet!"
Here is my albums_controller:
class AlbumsController < ApplicationController
before_action :set_album, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:edit, :new, :update, :destroy]
def index
#albums = Album.all
if params[:search].nil?
#albums = Album.all.order(year: :desc).order(title: :asc).paginate(:page => params[:page], :per_page => 24)
else
#albums = #albums.where("albums.title || albums.year || albums.artist LIKE ?", "%#{params[:search]}%").order(year: :desc).order(title: :asc).paginate(:page => params[:page], :per_page => 24)
end
end
def show
if #album.reviews.blank?
#average_review = 0
else
#average_review = #album.reviews.average(:rating).round(2)
end
#review = #album.reviews.build
end
def new
#album = Album.new
end
def edit
end
def create
#album = current_user.albums.build(album_params)
respond_to do |format|
if #album.save
format.html { redirect_to #album, notice: 'Album was successfully created.' }
format.json { render :show, status: :created, location: #album }
else
format.html { render :new }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #album.update(album_params)
format.html { redirect_to #album, notice: 'Album was successfully updated.' }
format.json { render :show, status: :ok, location: #album }
else
format.html { render :edit }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
def destroy
#album.destroy
respond_to do |format|
format.html { redirect_to albums_url, notice: 'Album was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_album
#album = Album.find(params[:id])
end
def album_params
params.require(:album).permit(:title, :artist, :year, :cover)
end
end
Here is the album show page, which is where the extra review is being displayed:
<div class="row">
<div class="col-xs-10 col-md-6 col-md-push-3 col-xs-push-1 top bottom">
<%= image_tag #album.cover, class: 'show_image' %>
<h2><%= #album.title %></h2>
<h4><%= #album.artist %></h4>
<h5><%= #album.year %></h5>
<p class="white">Average Review<div class="average-review-rating" data-score=<%= #average_review %>></div></p>
<% if user_signed_in? %>
<% if #album.user_id == current_user.id %>
<%= link_to 'Edit', edit_album_path(#album), class: 'grey' %>
| <%= link_to 'Delete', album_path, method: :delete, data: {confirm: "Are you sure?"}, class: 'grey' %>
<% end %>
<% end %>
<br /><br />
<h4>Reviews</h4>
<% if user_signed_in? %>
<p class="grey">Write a review</p>
<%= form_for [#album, #review] do |r| %>
<div class="form-group">
<div id="rating-form">
<label>Rating</label>
</div>
</div>
<div class="form-group">
<%= r.text_area :comment, class: 'form-control', placeholder: "Write a comment" %>
</div>
<div class="form-group">
<%= r.submit "Create", class: 'btn btn-success' %>
</div>
<% end %>
<% end %>
<br />
<% #album.reviews.each do |r| %>
<div class="red">
<div class="review-rating" data-number="<%= r.rating %>">
</div>
<p class="white"><%= r.comment %></p>
<% if user_signed_in? %>
<% if current_user.id == r.user_id %>
<%= link_to 'Edit', edit_album_review_path(#album, r.id), class: 'grey' %> |
<%= link_to 'Delete', album_review_path(#album, r.id), method: :delete, data: {confirm: "Are you sure?"}, class: 'grey' %>
<% end %>
<% end %>
<br /><br />
</div>
<% end %>
</div>
</div>
<script>
$('.review-rating').raty({
readOnly: true,
score: function() {
return $(this).attr('data-number');
},
path: '/assets/'
});
$('#rating-form').raty({
path: '/assets/',
scoreName: 'review[rating]'
});
$('.average-review-rating').raty({
readOnly: true,
path: '/assets/',
score: function() {
return $(this).attr('data-score')
}
});
</script>
Any help would be very appreciated!
The problem here is on the last line of your show method: #review = #album.reviews.build. This line not only creates a new Review instance and assigns it to #review, but it also adds that instance to the array in #album.reviews. So, in your view, when you iterate over #album.reviews, you will see all of the persisted reviews as well as the one new, not persisted review that was built with build.
The easiest way to fix this would be to add this line in your view:
<% #album.reviews.each do |r| %>
<% next unless r.persisted? %>
Another solution would be to associate the new review with the album, but not on the #album instance. In the controller:
#review = Review.new(album: #album)
I think it could be because of last line in show.
#review = #album.reviews.build
It builds an empty review.
I have a sidebar which displays the user's constructions.
I display it this way:
<% current_user.constructions.each do |obra| %>
<li>
<%= link_to construction_path(obra) do %>
<i class="fa fa-home"></i> <%= obra.name %>
<% end %>
<ul class="nav child_menu" style="display: block">
<li>
<%= link_to "Documentos", construction_docs_path(obra) %>
</li>
<li>
<%= link_to "Meus Pagamentos", construction_boletos_path(obra) %>
</li>
</ul>
</li>
<% end %>
It's working with everything, except when I try to create a New Construction, it says:
No route matches {:action=>"show", :controller=>"constructions", :id=>nil}
missing required keys: [:id]
I guess that's because it loses track of the current_user.constructions since my controller makes the
def new
#construction = current_user.constructions.build
end
call...
How can I pass the current constructions' ids, displaying it at the same time that I create a new construction?
EDIT -
constructions_controller.rb
class constructionsController < ApplicationController
before_filter :authenticate_user!
before_action :set_construction, only: [:show, :edit, :update, :destroy]
# GET /constructions
# GET /constructions.json
def index
#constructions = Construction.all
end
# GET /constructions/1
# GET /constructions/1.json
def show
end
# GET /constructions/new
def new
#construction = current_user.constructions.build
end
# GET /constructions/1/edit
def edit
end
# POST /constructions
# POST /constructions.json
def create
#construction = current_user.constructions.build(construction_params)
respond_to do |format|
if #construction.save
format.html { redirect_to #construction, notice: 'construction was successfully created.' }
format.json { render :show, status: :created, location: #construction }
else
format.html { render :new }
format.json { render json: #construction.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /constructions/1
# PATCH/PUT /constructions/1.json
def update
respond_to do |format|
if #construction.update(construction_params)
format.html { redirect_to #construction, notice: 'construction was successfully updated.' }
format.json { render :show, status: :ok, location: #construction }
else
format.html { render :edit }
format.json { render json: #construction.errors, status: :unprocessable_entity }
end
end
end
# DELETE /constructions/1
# DELETE /constructions/1.json
def destroy
#construction.destroy
respond_to do |format|
format.html { redirect_to constructions_url, notice: 'construction was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_construction
#construction = Construction.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def construction_params
params.require(:construction).permit(:name, :locale, :start_date, :description, :image)
end
end
routes.rb
Rails.application.routes.draw do
resources :boletos
resources :documentos
resources :constructions do
get 'construction_documents/index', :path => 'docs', :as => 'docs'
get 'construction_boletos/index', :path => 'boletos', :as => 'boletos'
end
devise_for :users
root 'plainpage#index'
end
constructions/new.html.erb
<h1>New Construction</h1>
<%= render 'form' %>
<%= link_to 'Back', constructions_path %>
constructions/_form.html.erb
<%= form_for #construction, html: { multipart: true } do |f| %>
<% if #construction.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#construction.errors.count, "error") %> prohibited this construction from being saved:</h2>
<ul>
<% #construction.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.file_field :image %>
</div>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :locale %><br>
<%= f.text_field :locale %>
</div>
<div class="field">
<%= f.label :start_date %><br>
<%= f.date_select :start_date %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I'm trying to build a small expense tracking app. Using the nested_form gem to add line items. There is an Expense model which accepts nested attributes. Items belong to expenses and there is a foreign key association. Here's the expense controller:
class ExpensesController < ApplicationController
def new
#expense = Expense.new
#item = #expense.items.build
end
def index
#expenses = Expense.all
##items = Item.where(:expense_id => #expense.id)
end
def show
#expense = Expense.find(params[:id])
#items = Item.where(:expense_id => #expense.id)
end
def create
#expense = Expense.new(expense_params)
respond_to do |format|
if #expense.save
format.html { redirect_to #expense, notice: 'Expense Report Submitted.' }
format.json { render :show, status: :created, location: #expense }
else
format.html { render :new }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
def edit
#expense = Expense.find(params[:id])
end
def update
#expense = Expense.find(params[:id])
if #expense.save(expense_params)
flash[:notice] = "Expense Report Updated"
redirect_to #expense
else
render 'edit'
end
end
def destroy
#expense = Expense.find(params[:id])
#expense.destroy
redirect_to 'root'
end
private
def expense_params
params.require(:expense).permit(:department_id, :expense_type_id, :notes, items_attributes: [:id, :description, :amount, :issue_date, :_destroy])
end
end
The form looks like:
<%= nested_form_for (#expense) do |f| %>
<% if #expense.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#expense.errors.count, "error") %> prohibited
this expense from being saved:</h2>
<ul>
<% #expense.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class"row">
<div class="col-md-8">
<div class="form-group">
<%= f.label :department_id %><br>
<%= f.collection_select(:department_id, Department.all, :id, :department_name, prompt: true, class: "dropdown-menu") %>
</div>
<div class="form-group">
<%= f.label :expense_type_id %><br>
<%= f.collection_select(:expense_type_id, ExpenseType.all, :id, :expense_name, prompt: true, class: "form-control") %>
</div>
<%= f.fields_for :items do |i| %>
<div class="form-group">
<%= i.label :description%>
<%= i.text_field :description, class: "form-control" %>
</div>
<div class="form-group">
<%= i.label :amount%>
<%= i.text_field :amount, class: "form-control" %>
</div>
<div class="form-group">
<%= i.label :issue_date%>
<%= i.date_select :issue_date, class: "form-control" %>
</div>
<%= i.link_to_remove "Remove", class: "btn btn-default" %>
<% end %>
<div><p><%= f.link_to_add "Add Expense", :items, class: "btn btn-default" %></p></div>
<div class="form-group">
<%= f.label :notes %>
<%= f.text_area :notes, class: "form-control" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
When I hit submit, the expense and items are saved. I checked with Sqlite browser and the foreign key values are captured for each item.
The index file looks like:
<% #expenses.each do |expense| %>
<tr>
<td><%= expense.item.description %></td>
<td><%= number_to_currency(expense.item.amount) %></td>
<td><%= expense.item.issue_date %></td>
<% end %>
I tried the usual combinations (like expense.item.description) by passing through a block, but they aren't working. I would like to know how to display the expense and the associated items in the show and index pages.
Looking at your new action, in your models you would have
class Expense < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
end
So when you are doing expense.items in your view it gives you an active record relation(since expense has many items), try this:
<% #expenses.each do |expense| %>
<% expense.items.each do |item| %>
<tr>
<td><%= item.description %></td>
<td><%= number_to_currency(item.amount) %></td>
<td><%= item.issue_date %></td>
</tr>
<% end %>
<% end %>
I am fairly new to Rails 4, and I am creating a Q&A site similar to Stack Overflow.
I have a question page much like the one on Stack Overflow. The question part is working, and then I have answers, which are working too, HOWEVER, I want to be able to have the answerer's name next to their answer.
I am pretty sure I have the controller, model and view set up properly. I have reset the database but it still comes up with errors.
Here's my code:
#Answers_controller.rb create action
def create
#answer = #question.answers.new(answer_params)
#answer.user_id = current_user.username
respond_to do |format|
if #answer.save
format.html { redirect_to [#question], notice: 'Answer was successfully created.' }
format.json { render action: 'show', status: :created, location: #answer }
else
format.html { render action: 'new' }
format.json { render json: #answer.errors, status: :unprocessable_entity }
end
end
end
answer.rb (model):
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question
validates :body, presence: true
end
#Then in user.rb I have has_many :answers, has_many :questions
#Also in question.rb has_many :answers
answers/_answer.html.erb:
<%= answer.username %>
<%= answer.body %>
</p>
<p>
<% if current_user.present? && current_user == answer.user %>
<%= link_to 'Delete Answer', [answer.question, answer],
method: :delete,
data: { confirm: 'Are you sure?' } %>
<% end %>
</p>
Then in questions/show:
<div class="container">
<div class="row">
<div class="span8">
<h3><%= #question.title %>
<% if current_user.present? && current_user == #question.user %>
<%= link_to 'Edit', edit_question_path(#question) %></h3>
<% end %>
<hr>
</div>
</div>
<div class="row">
<div class="span8">
<p><%= #question.description %></p>
Asked by <%= #question.user.full_name %>
<hr>
</div>
</div>
<%= render "answers/form" %>
<%= render #question.answers %>
</div>
<script type="text/javascript">
$(document).ready(function(){
$('.wysihtml5').each(function(i, elem) {
$(elem).wysihtml5();
});
})
</script>
It looks like you wrong here
#answer = #question.answers.new(answer_params)
try
#answer = #question.answers.build(answer_params)
Explanaition: you build a new answer but do not refer it to question because according to has_many documentation it has collection.build(attributes = {}, …) not collection.new(attributes).
A couple of things that stand out:
#Answers_controller.rb create action
def create
#answer = #question.answers.new(answer_params)
#answer.user_id = current_user.username # ???? should be current_user.id ????
# omitted
end
answers/_answer.html.erb
<%= answer.username %> # ???? you have no username field on your answer model ???
# ??? should be <%= answer.user.username %> ???
<%= answer.body %>
</p>
# omitted