In the show view for the 'service' model I want to be able to show all of the 'reviews' that are associated with this service. Need to know what to put in the 'service' show view as well as the 'service'/'review' controller.
Service model:
class Service < ActiveRecord::Base
has_many :reviews
end
Review model:
class Review < ActiveRecord::Base
has_one :service
belongs_to :service
end
Service show view:
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Review</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #reviews.each do |review| %>
<tr>
<td><%= review.name %></td>
<td><%= review.date %></td>
<td><%= review.review %></td>
<td><%= link_to 'Show', review %></td>
<td><%= link_to 'Edit', edit_review_path(review) %></td>
<td><%= link_to 'Destroy', review, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
Review schema:
create_table "reviews", force: true do |t|
t.string "name"
t.string "date"
t.text "review"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "service_id"
end
Review controller:
def index
#reviews = Review.all
respond_with(#reviews)
end
def show
respond_with(#review)
end
def new
#review = Review.new
respond_with(#review)
end
def edit
end
def create
#review = Review.new(review_params)
#review.save
respond_with(#review)
end
Service controller:
def new
#service = Service.new
end
# GET /services/1/edit
def edit
end
# POST /services
# POST /services.json
def create
#service = Service.new(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render :show, status: :created, location: #service }
else
format.html { render :new }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
If you want to show all of the reviews that are associated with the service in the service show page then, you need to tweak your service_controller show action like this
def show
#service = Service.find(params[:id])
#reviews = #service.reviews
end
I don't really get what exactly are you trying to accomplish. If you want to access the reviews for a certain service in the show view, you can do that by simply doing
#reviews = #service.reviews
That's if you have a #service object in your service controller.
If that's not something you needed please edit your question and provide a clearer explanation.
Related
So my goal with this method is to have it link to customers/1/showcar similar to how it will link to customers/1/edit, which is how I'm attempting to model my code.
My controller is
class CustomersController < ApplicationController
before_action :set_customer, only: [:show, :edit, :update, :destroy, :showcar]
# GET /customers
# GET /customers.json
def index
#customers = Customer.all
end
# GET /customers/1
# GET /customers/1.json
def show
end
# GET /customers/1/showcar
def showcar
end
# GET /customers/new
def new
#customer = Customer.new
end
# GET /customers/1/edit
def edit
end
# POST /customers
# POST /customers.json
def create
#customer = Customer.new(customer_params)
respond_to do |format|
if #customer.save
format.html { redirect_to #customer, notice: 'Customer was successfully created.' }
format.json { render :show, status: :created, location: #customer }
else
format.html { render :new }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /customers/1
# PATCH/PUT /customers/1.json
def update
respond_to do |format|
if #customer.update(customer_params)
format.html { redirect_to #customer, notice: 'Customer was successfully updated.' }
format.json { render :show, status: :ok, location: #customer }
else
format.html { render :edit }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
# DELETE /customers/1
# DELETE /customers/1.json
def destroy
#customer.destroy
respond_to do |format|
format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_customer
#customer = Customer.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def customer_params
params.require(:customer).permit(:cust_id, :cust_fname, :cust_lname, :cust_phone, :cust_addr, :cust_date)
end
end
and my html.erb file where I'm attempting to call the method is
<style>
th, td{
padding-left: 20px;
}
</style>
<p id="notice"><%= notice %></p>
<h1>Customers</h1>
<table>
<thead>
<tr>
<th>Cust ID</th>
<th>Cust fname</th>
<th>Cust lname</th>
<th>Cust phone</th>
<th>Cust addr</th>
<th>Cust date</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #customers.each do |customer| %>
<% belongstocust = Car.where(cust_id: customer.cust_id) %>
<tr>
<td><%= customer.cust_id %></td>
<td><%= customer.cust_fname %></td>
<td><%= customer.cust_lname %></td>
<td><%= customer.cust_phone %></td>
<td><%= customer.cust_addr %></td>
<td><%= customer.cust_date %></td>
<td><%= link_to 'Show', customer %></td>
<td><%= link_to 'Edit', edit_customer_path(customer) %></td>
<td><%= link_to 'Destroy', customer, method: :delete, data: { confirm: 'Are you sure?' } %></td>
#placeholder, not permanent code
<% i = '' %>
<% belongstocust.each do |car| %>
<% i = car.car_model %>
<td><%= link_to 'Show ' + i, car_path(car) %></td>
<% end %>
<td><%= link_to 'Show Car', showcar_customer_path(customer) %> </td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Customer', new_customer_path %>
<br>
<%= link_to 'Home', home_index_path %>
The issue is, whenever I have the code <td><%= link_to 'Show Car', showcar_customer_path(customer) %> </td>, I get the noMethodError even though the method is defined inside the controller, and it looks the exact same as def show and def edit. I've tried making a controller called customer, and adding the showcar method to that controller, and it worked, but it wouldn't pass over the customer. I also tried adding showcar to customer.rb, but it also gave me a noMethodError. I'm a complete noob when it comes to ruby, and was just told to make a project using the framework, so I've been having to learn along the way. This could be a really simple issue that I don't know how to solve due to my ignorance, so if that's the case I'm sorry.
I get the noMethodError even though the method is defined inside the controller
No. You have showcar defined, not showcar_customer_path. You are missing a route, the thing that defines xxx_path methods.
In your config/routes.rb you probably have
resources :customers
To register this new action, you can do
resources :customers do
member do
get :showcar
end
end
Now showcar_customer_path should be available to use in the views.
i am learning rails and creating a web app which also got ecommerce in it
There is a Form which user can fill only if he is logged in, For that i was using Devise, then for e-commerce i installed Spree
Spree got its own login authentication, and there is no authenticate_user! in controllers too,
i removed devise and having a tough time finding how to use Spree's authentication with my Form
here is UPDATED Form's controller:
complaints_controller.rb
module Spree
class ComplaintsController < Spree::StoreController
before_action :require_login
before_action :set_complaint, only: [:show, :edit, :update, :destroy]
# GET /complaints
# GET /complaints.json
def require_login
redirect_to spree_login_path unless current_spree_user
end
def index
#complaints = Complaint.all
end
# GET /complaints/1
# GET /complaints/1.json
def show
end
# GET /complaints/new
def new
#complaint = Complaint.new
end
# GET /complaints/1/edit
def edit
end
# POST /complaints
# POST /complaints.json
def create
#complaint = Complaint.new(complaint_params)
respond_to do |format|
if #complaint.save
format.html { redirect_to #complaint, notice: 'Complaint was successfully created.' }
format.json { render :show, status: :created, location: #complaint }
else
format.html { render :new }
format.json { render json: #complaint.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /complaints/1
# PATCH/PUT /complaints/1.json
def update
respond_to do |format|
if #complaint.update(complaint_params)
format.html { redirect_to #complaint, notice: 'Complaint was successfully updated.' }
format.json { render :show, status: :ok, location: #complaint }
else
format.html { render :edit }
format.json { render json: #complaint.errors, status: :unprocessable_entity }
end
end
end
# DELETE /complaints/1
# DELETE /complaints/1.json
def destroy
#complaint.destroy
respond_to do |format|
format.html { redirect_to complaints_url, notice: 'Complaint was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_complaint
#complaint = Complaint.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def complaint_params
params.require(:complaint).permit(:id_society, :id_user, :heading, :text, :active, :action, :IsDelete, :flat_number)
end
end
end
<% end %>
index.html.erb
<% if spree_current_user %>
<p id="notice"><%= notice %></p>
<h1>Listing Complaints</h1>
<table>
<thead>
<tr>
<th>Id society</th>
<th>Id user</th>
<th>Heading</th>
<th>Text</th>
<th>Active</th>
<th>Action</th>
<th>Isdelete</th>
<th>Flat number</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #complaints.each do |complaint| %>
<tr>
<td><%= complaint.id_society %></td>
<td><%= complaint.id_user %></td>
<td><%= complaint.heading %></td>
<td><%= complaint.text %></td>
<td><%= complaint.active %></td>
<td><%= complaint.action %></td>
<td><%= complaint.IsDelete %></td>
<td><%= complaint.flat_number %></td>
<td><%= link_to 'Show', complaint %></td>
<td><%= link_to 'Edit', edit_complaint_path(complaint) %></td>
<td><%= link_to 'Destroy', complaint, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Complaint', new_complaint_path %>
<% else %>
<h1> please login</h1>
<% end %>
This works, as it verifies user's authentication in View, is there any way to check it in controller? Like if user is logged in it will be sent to action or else redirected to login?
Thank you
Spree uses devise authentication through a extension:
https://github.com/spree/spree_auth_devise
For authenticate your actions at controller(your own controllers) level, you need to define your own authentication filter. So you can manage something like this:
before_action :require_login
def require_login
redirect_to login_url unless current_spree_user
end
In my Ruby on Rails application users are able to leave reviews for products, and then the administrator can view all reviews. What I want to be able to do is make it so that an administrator can still see all reviews left by everyone but a normal user can only view their own reviews and not everyone elses. Is there a simple way I can do this without using java?
Below is my index.html.erb that displays all reviews by everyone.
<div class="centre-content">
<div class="main-title">All reviews:</div>
<table>
<tr>
<th>Product</th>
<th>Name</th>
<th>Review text</th>
<th>No of stars</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #reviews.each do |review| %>
<tr>
<td><%= review.product.title %></td>
<td><%= review.user.name %></td>
<td><%= review.review_text.truncate(35) %></td>
<td><%= review.no_of_stars %></td>
<td><%= link_to 'Show', review %></td>
<td><%= link_to 'Edit', edit_review_path(review) %></td>
<td><%= link_to 'Destroy', review, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'Back', :back %>
</div>
application.html.erb where the administrator can click to view all reviews:
<% if admin? %>
<li> <%= link_to "Users", "" , :class => active_menu("users") %>
<ul>
<li> <%= link_to "Users" , users_path %> </li>
<li> <%= link_to "Edit profile" , edit_user_path(session[:user_id]) %> </li>
</ul>
<li> <%= link_to "Categories", categories_path , :class => active_menu("categories") %>
<li> <%= link_to "Reviews", reviews_path , :class => active_menu("reviews") %>
<% end %>
reviews_controller.rb:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
# GET /reviews
# GET /reviews.json
def index
#reviews = Review.all
end
# GET /reviews/1
# GET /reviews/1.json
def show
end
def new
if logged_in?
existing_review = Review.find_by_user_id_and_product_id(session[:user_id], params[:id])
if existing_review == nil
#review = Review.new(product_id: params[:id],
user_id: User.find(session[:user_id]).id)
session[:return_to] = nil
else
redirect_to edit_review_path(existing_review.id)
end
else
session[:return_to] = request.url
redirect_to login_path, alert: "You need to login to write a review"
end
end
# GET /reviews/1/edit
def edit
end
def create
#review = Review.new(review_params)
if #review.save
product = Product.find(#review.product.id)
redirect_to product, notice: 'Your review was successfully added.'
else
render action: 'new'
end
end
# PATCH/PUT /reviews/1
# PATCH/PUT /reviews/1.json
def update
respond_to do |format|
if #review.update(review_params)
format.html { redirect_to #review, notice: 'Review was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #review.errors, status: :unprocessable_entity }
end
end
end
# DELETE /reviews/1
# DELETE /reviews/1.json
def destroy
#review.destroy
respond_to do |format|
format.html { redirect_to reviews_url }
format.json { head :no_content }
end
end
def displays
product = Product.find(params[:id])
#reviews = product.reviews
if #reviews.empty?
redirect_to product, notice: "No reviews - as yet ..."
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_review
#review = Review.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def review_params
params.require(:review).permit(:product_id, :user_id, :review_text, :no_of_stars)
end
end
reviews.rb model:
class Review < ActiveRecord::Base
belongs_to :product
belongs_to :user
validates :review_text, :presence => { :message => "Review text: cannot be blank ..."}
validates :review_text, :length => {:maximum => 2000, :message => "Review text: maximum length 2000 characters"}
validates :no_of_stars, :presence => { :message => "Stars: please rate this book ..."}
end
Is there anyway I can make it so that a user only views their own reviews?
In your controller display method, you can do stg like this i think :
def display
product = Product.find(params[:id])
#reviews = product.reviews.select! { |s| s.user_id == current_user.id } unless admin?
....
end
That way, only admin will have all reviews, and others users will only see their own reviews (with theirs ids)
you dont have current_user method but it es explained well in this tutorial :
https://www.railstutorial.org/book/log_in_log_out#sec-current_user
it is the same as session[:id] but in a well ordered manner.
Ps: i'm not sur about the s.user_id ... don't remember the exact syntax but should be it.
Another way :
you could take the reviews for this specific product id and then filter out by the user id.
`#reviews = Reviews.find_by_product_id(params[:product_id]).select { |r| r.user_id == current_user.id }`
I use rails 4 and define one-one relationship between model book and isbn, but can not access the nested attribute. Below is the code
book.rb
class Book < ActiveRecord::Base
has_one :isbn
accepts_nested_attributes_for :isbn
end
isbn.rb
class Isbn < ActiveRecord::Base
belongs_to :book
end
books_controller.rb
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
# GET /books
# GET /books.json
def index
#books = Book.find_by_sql(["select * from books where price > ?", 20])
end
# GET /books/1
# GET /books/1.json
def show
end
# GET /books/new
def new
#book = Book.new
end
# GET /books/1/edit
def edit
end
# POST /books
# POST /books.json
def create
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render action: 'show', status: :created, location: #book }
else
format.html { render action: 'new' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /books/1
# PATCH/PUT /books/1.json
def update
respond_to do |format|
if #book.update(book_params)
format.html { redirect_to #book, notice: 'Book was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# DELETE /books/1
# DELETE /books/1.json
def destroy
#book.destroy
respond_to do |format|
format.html { redirect_to books_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_book
#book = Book.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def book_params
params.require(:book).permit(:name, :author, :price, isbn_attributes: [:number])
end
end
books\index.html.erb
<table>
<tr>
<th>#</th>
<th>name</th>
<th>author</th>
<th>price</th>
<th>ISBN</th>
<th colspan="3">operation</th>
</tr>
<% #books.each do |book| %>
<tr>
<td><%= book.id %></td>
<td><%= book.name %></td>
<td><%= book.author %></td>
<td><%= book.price %></td>
<td><%= book.isbn.number %></td>
<td><%= link_to 'detail', book %></td>
<td><%= link_to 'edit', edit_book_path(book) %></td>
<td><%= link_to 'del', book, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
but the "<%= book.isbn.number %>" throw exception: undefined method `number' for nil:NilClass.
How to fix this issue? Thanks!
The simplest way is to check for isbn.nil? before accessing its attributes.
But there are better ways of doing this.
Using delegate so that don't violate the Law of Demeter.
class Book < ActiveRecord::Base
has_one :isbn
accepts_nested_attributes_for :isbn
delegate :number, to: :isbn, allow_nil: true, prefix: true
end
# then in your view
book.isbn_number
Using decorators, draper being the most popular gem.
class BookDecorator < Draper::Decorator
delegate_all
def isbn_number
# view can be more complex
isbn.number unless isbn.nil?
end
end
# don't forget to wrap your book instance in controller
Decorators are used to accumulate view logic. Use them when you need something more complex than a simple check for existence. Also don't make make your models too fat with a lot of delegators/methods, use decorators instead.
In rare cases you may need your associations always built/created when parent was. You can do it in your controller. Also you can use ActiveRecord callbacks and build associations on parent initialisation which is not recommended because there are some side effects, in tests primarily.
This happened because for that particular book no isbn exists, to handle this add a condition to <%= book.isbn.number %>
<td><%= book.isbn.number unless book.isbn.blank? %></td>
Alright so I'm pretty new to this rails stuff so please bear with me...
I'm trying to make the most simple application ever, a Christmas list, and I need a little bit of help. Let me fill you in:
I scaffolded a person and an item. I modified my models a little bit and here is what they look like.
class Person < ActiveRecord::Base
has_many :item, :dependent => :destroy
end
class Item < ActiveRecord::Base
belongs_to :person
end
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.integer :person_id
t.string :description
t.timestamps
end
end
end
class CreatePeople < ActiveRecord::Migration
def change
create_table :people do |t|
t.string :name
t.timestamps
end
end
end
Seems like that's all cool. The index action on the people_controller lists all the people(duh)
<% #people.each do |person| %>
<tr>
<td><%= link_to person.name, "/people/#{person.id}" %></td>
</tr>
<% end %>
and when you click on one, it calls the show action(same controller) which gets all of the items for that person
def show
#person = Person.find(params[:id])
#items = Item.where(:person_id => params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #person }
end
end
and pulls the show view
<table>
<% #items.each do |item| %>
<tr>
<td><%= item.description %></td>
<td><%= link_to 'Edit', edit_item_path %></td>
<td><%= link_to 'Remove', "" %></td>
</tr>
<% end %>
</table>
<br/>
<%= link_to 'Add', :controller => :items, :action => :new, :id => #person.id %>
The link at the bottom is to add a new item for the person who's summary we are viewing. So then in the new action on the items_controller I have:
def new
#item = Item.new
#item.person_id = params[:id]
respond_to do |format|
format.html # new.html.erb
format.json { render json: #item }
end
end
Now I know this doesn't get saved until #item.save is called. I imagine this happens from the _form.html.erb submit button which in turn calls the create action in the controller?
def create
#item = Item.new(params[:item])
respond_to do |format|
if #item.save
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: "new" }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
I'm just a little confused why this is never getting set, it seems like it should be so easy(I'm sure it is haha). Also while I'm at it, you may have noticed I have no link for my 'Remove' link above. This is because I also couldn't figure out how to destroy action from that link to remove the correct item.
Like I said, this is all new to me. I appreciate any help! Please feel free to critique EVERYTHING I have done here. I don't have feelings :)
As Vibhu said, it's quite likely your issue stem from the fact that you should have has_many :items (note the plural) in your Person controller.
To add a hidden filed in your form specifying the person's id, add this in your creation form:
f.hidden_field :person_id