My goal is to display the 'edit' and 'delete' buttons only to the user who created the listing. However, for some reason, current_user is returning nil. Any idea why this is happening?
Here is my user model:
class User < ActiveRecord::Base
has_many :listings
has_many :thoughts
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Here is my listing model:
class Listing < ActiveRecord::Base
belongs_to :user
has_many :thoughts
end
<% #listings.each do |listing| %>
<tr>
<td><%= listing.title %></td>
<td><%= listing.school %></td>
<td><%= listing.price %></td>
<td><%= listing.description %></td>
<% if current_user == listing.user %>
<td><%= link_to 'Show', listing %></td>
<td><%= link_to 'Edit', edit_listing_path(listing) %></td>
<td><%= link_to 'Delete', listing, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% else %>
<td><%= link_to 'Show', listing %></td>
<% end %>
</tr>
<% end %>
Here is the create action in the Listing controller
def create
#listing = Listing.new(listing_params)
respond_to do |format|
if #listing.save
format.html { redirect_to #listing, notice: 'Listing was successfully created.' }
format.json { render action: 'show', status: :created, location: #listing }
else
format.html { render action: 'new' }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
Here is my create_listings migration
class CreateListings < ActiveRecord::Migration
def change
create_table :listings do |t|
t.string :title
t.string :school
t.integer :price
t.text :description
t.timestamps
end
end
end
Make sure you have before_filter :authenticate_user! set in your controller:
class ListingsController < ActionController::base
before_filter :authenticate_user!
def index
#listings = Listing.all
end
end
As for your create method, so long as the table has a user_id column you just need to set the user for that listing:
def create
#listing = Listing.new(listing_params)
#listing.user = current_user
respond_to do |format|
if #listing.save
format.html { redirect_to #listing, notice: 'Listing was successfully created.' }
format.json { render action: 'show', status: :created, location: #listing }
else
format.html { render action: 'new' }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
Lastly, for the listing to belong to a user it needs to be able to record that users id. Make sure your table has a user_id column:
class CreateListings < ActiveRecord::Migration
def change
create_table :listings do |t|
t.string :title
t.string :school
t.integer :price
t.text :description
t.integer :user_id
t.timestamps
end
end
end
You can re-run this migrate if it is your latest by calling rake db:migrate:redo from the console.
If it isn't the latest you need to run the down and up specifically with VERSION=xxx to for that migrates ID (follow the steps here: 4.3 Running Specific Migrations). THIS WILL EMPTY THE TABLE.
If you need to keep date in that table then you need to write a new migrate with just the command add_column :listings, :user_id, :integer.
I'd do something likesigned_in? && current_user.id == listing.user_id
Related
I'm relatively new to Rails. I'm trying to create an application that can allow users to create video game items and store them under their own users. I'm using the latest version of Rails and Devise.
Using scaffolding as a base, I created the Videogame model/controller within my application. After linking the video game models to the user who created them, it seems that any attributes that are entered into the creation form are not saving, or at the very least just not showing up on the videogames/index page. After trying to search around on Google and StackOverflow, I couldn't find any similar questions/guides to work with.
Any ideas on how to fix this? Any help for a Rails newbie would be greatly appreciated.
Below I've posted all files that may be relevant. Please let me know if anything else is needed. To see the whole project, see http://github.com/bmmart2/collection-manager
Image after item creation
Index page of two created items
Here is my controller:
class VideogamesController < ApplicationController
before_action :set_videogame, only: [:show, :edit, :update, :destroy]
# GET /videogames
# GET /videogames.json
def index
if user_signed_in?
#videogame = current_user.videogames.all
else
redirect_to :root
end
end
# GET /videogames/1
# GET /videogames/1.json
def show
end
# GET /videogames/new
def new
#videogame = current_user.videogames.new
end
# GET /videogames/1/edit
def edit
end
# POST /videogames
# POST /videogames.json
def create
#videogame = current_user.videogames.create(videogame_params)
respond_to do |format|
if #videogame.save
format.html { redirect_to #videogame, notice: 'Videogame was successfully created.' }
format.json { render :show, status: :created, location: #videogame }
else
format.html { render :new }
format.json { render json: #videogame.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /videogames/1
# PATCH/PUT /videogames/1.json
def update
respond_to do |format|
if #videogame.update(videogame_params)
format.html { redirect_to #videogame, notice: 'Videogame was successfully updated.' }
format.json { render :show, status: :ok, location: #videogame }
else
format.html { render :edit }
format.json { render json: #videogame.errors, status: :unprocessable_entity }
end
end
end
# DELETE /videogames/1
# DELETE /videogames/1.json
def destroy
#videogame.destroy
respond_to do |format|
format.html { redirect_to videogames_url, notice: 'Videogame was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_videogame
#videogame = Videogame.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def videogame_params
params.require(:videogame).permit(:title, :publisher, :platform, :year, :condition, :upc)
end
end
Videogame model:
class Videogame < ApplicationRecord
belongs_to :user
attr_accessor :title, :platform, :upc, :condition, :publisher, :year
end
Videogame db migration file:
class CreateVideogames < ActiveRecord::Migration[5.2]
def change
create_table :videogames do |t|
t.string :title
t.string :publisher
t.integer :condition
t.string :platform
t.string :year
t.string :upc
t.timestamps
end
add_index :videogames, :user_id
end
end
add_user_refs_to_videogame migration:
class AddUserRefsToVideogame < ActiveRecord::Migration[5.2]
def change
add_reference :videogames, :user, foreign_key: true
end
end
Edit: show view for video game
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #videogame.title %>
</p>
<p>
<strong>Publisher:</strong>
<%= #videogame.publisher %>
</p>
<p>
<strong>Platform:</strong>
<%= #videogame.platform %>
</p>
<p>
<strong>Year:</strong>
<%= #videogame.year %>
</p>
<p>
<strong>Condition:</strong>
<%= #videogame.condition %>
</p>
<p>
<strong>Upc:</strong>
<%= #videogame.upc %>
</p>
<%= link_to 'Edit', edit_videogame_path(#videogame) %> |
<%= link_to 'Back', videogames_path %>
I believe the attr_accessor line in your videogame.rb file is causing the problem. Try deleting it and see if that fixes the problem.
I checked various solutions that are available, but none seem to address the issue that I am having with my project. I am trying to allow the user to create reviews through the EmployerReview model for the employer. When I pass the employer_id to the form_for in the employer_reviews_controller it claims that it cannot find an employer without an ID. The id is being passed with the #employer instance variable. I don't get why it's not working. Lastly, I am using friendly_id, and it shows at the end of the new employer review address employer_reviews/new.bryers. How can I stop this error from occurring?
schema
create_table "employer_reviews", force: :cascade do |t|
t.text "review_body"
t.string "review_title"
t.bigint "user_id"
t.bigint "employer_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["employer_id"], name: "index_employer_reviews_on_employer_id"
t.index ["user_id"], name: "index_employer_reviews_on_user_id"
end
employer_review.rb
class EmployerReview < ApplicationRecord
belongs_to :employer
belongs_to :user
end
user.rb
class User < ApplicationRecord
has_many :employer_reviews, dependent: :destroy
end
employer.rb
class Employer < ApplicationRecord
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
has_many :employer_reviews, dependent: :destroy
end
employers_controller.rb
class EmployersController < ApplicationController
before_action :set_employer, only: [:show]
def show
impressionist(#employer)
end
private
def set_employer
#employer = Employer.find(params[:id])
end
end
employer_reviews_controller.rb
class EmployerReviewsController < ApplicationController
before_action :set_employer_review, only: [:show, :edit, :update, :destroy]
before_action :set_employer
before_action :authenticate_user!
# GET /employer_reviews/1
# GET /employer_reviews/1.json
def show
end
# GET /employer_reviews/new
def new
#employer_review = EmployerReview.new
end
# GET /employer_reviews/1/edit
def edit
end
# POST /employer_reviews
# POST /employer_reviews.json
def create
#employer_review = EmployerReview.new(employer_review_params)
#employer_review.user_id = current_user.id
#employer_review.employer_id = #employer_review.id
respond_to do |format|
if #employer_review.save
format.html {redirect_to #employer_review, notice: 'Employer review was successfully created.'}
format.json {render :show, status: :created, location: #employer_review}
else
format.html {render :new}
format.json {render json: #employer_review.errors, status: :unprocessable_entity}
end
end
end
# PATCH/PUT /employer_reviews/1
# PATCH/PUT /employer_reviews/1.json
def update
respond_to do |format|
if #employer_review.update(employer_review_params)
format.html {redirect_to #employer_review, notice: 'Employer review was successfully updated.'}
format.json {render :show, status: :ok, location: #employer_review}
else
format.html {render :edit}
format.json {render json: #employer_review.errors, status: :unprocessable_entity}
end
end
end
# DELETE /employer_reviews/1
# DELETE /employer_reviews/1.json
def destroy
#employer_review.destroy
respond_to do |format|
format.html {redirect_to employer_reviews_url, notice: 'Employer review was successfully destroyed.'}
format.json {head :no_content}
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_employer_review
#employer_review = EmployerReview.find(params[:id])
end
def set_employer
#employer = Employer.find(params[:employer_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def employer_review_params
params.require(:employer_review).permit([:review_title, :review_body])
end
end
employer show.html.erb
<%= link_to 'Write a review', new_employer_review_path(#employer) %>
routes.rb
resources :employers
resources :employer_reviews
review form
<%= simple_form_for([#employer, #employer_review]) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<label>Review Title</label>
<%= f.input :review_title, class: 'form-control', placeholder: 'Add a review title' %>
</div>
<div class="form-group">
<label>Tell us about your experience</label>
<%= f.input :review_body, :as => :text, :input_html => { 'rows' => 5, 'cols' => 10 }, class: 'form-control', placeholder: 'Add a review title' %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
UPDATE 1:
I'm receiving the error below when I use both #employer_review and #employer in the simple_form.
ActionView::Template::Error (undefined method `model_name' for nil:NilClass):
1: <%= simple_form_for([#employer_review, #employer]) do |f| %>
2: <%= f.error_notification %>
3: <div class="form-group">
4: <label>Review Title</label>
app/views/employer_reviews/_form.html.erb:1:in `_app_views_employer_reviews__form_html_erb__154747394_155744960'
app/views/employer_reviews/new.html.erb:3:in `_app_views_employer_reviews_new_html_erb__376973310_155859880'
Processing by ExceptionHandler::ExceptionsController#show as HTML
Parameters: {"employer_id"=>"test_employer"}
Completed 500 Internal Server Error in 404ms (ActiveRecord: 0.0ms)
Your employers and employer_reviews resources are not nested. It means employer_id param in your EmployerReviewsController is nil (if it is not set explicitly in query string). Calling set_employer filter has no effect and #employer instance variable is nil. That's why simple_form cannot find that employer (without id).
You either have to turn employer_reviews to nested resource of employers, or remove #employer variable from EmployerReviewsController and use simple_form solely for #employer_review.
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.
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