I am using Rails 4.1.6
I am trying to create ability to select at least 3 services and then save with advertisement.
Model structure is like this:
class Service < ActiveRecord::Base
belongs_to :advertisement
end
class Advertisement < ActiveRecord::Base
has_many :services
end
My form:
<% #services.each do |service| %>
<li>
<%= check_box_tag 'service_ids[]', service.id -%>
<%= h service.name -%>
</li>
<% end %>
Controller:
class AdvertisementsController < ApplicationController
before_action :authenticate_user!
before_action :set_advertisement, only: [:show, :edit, :update, :destroy]
respond_to :html, :xml, :json
def index
#advertisements = Advertisement.all
respond_with(#advertisements)
end
def show
#advertisement = Advertisement.find(params[:id])
respond_with(#advertisement)
end
def new
#advertisement = Advertisement.new
#services = Service.all
respond_with(#advertisement)
end
def edit
end
def create
#advertisement = Advertisement.new(advertisement_params)
if #advertisement.save
if params[:images]
params[:images].each { |image|
#advertisement.pictures.create(image: image)
}
respond_with(#advertisement)
end
else
format.html { render 'new'} ## Specify the format in which you are rendering "new" page
format.json { render json: #advertisement.errors } ## You might want to specify a json format as well
end
end
def update
#advertisement.update(advertisement_params)
respond_with(#advertisement)
end
def destroy
#advertisement.destroy
respond_with(#advertisement)
end
private
def set_advertisement
#advertisement = Advertisement.find(params[:id])
end
def advertisement_params
params.require(:advertisement).permit(:name, :user_id, :advertisement_id, :image,:terms_of_service,:region, :age, :height, :phone_number,:description)
end
end
When I check multpile services and click save there is no error. When I checked log files i got this message "
Parameters: ... "service_ids"=>["1", "2", "3"] ...
But then in console I check:
2.1.3 :008 >t = Advertisement.last (ALL OK)
....
2.1.3 :008 > t.services
=> #<ActiveRecord::Associations::CollectionProxy []>
And this is error. Problem in associations, but everyrhing seems ok to me.
What could be the problem?
Thanks
There are no service_ids in advertisement_params. Add them:
#advertisement = Advertisement.new(advertisement_params.merge(service_ids: params[:service_ids]))
# or
#advertisement = Advertisement.new(advertisement_params)
#advertisement.service_ids = service_ids
Related
I have two model called TodoList and TodoItem. In the TodoItem index page, i'm showing new form and list of todo items. Everything works perfect But it generate an empty record while in browser.
class TodoItem < ApplicationRecord
belongs_to :todo_list
end
class TodoList < ApplicationRecord
has_many :todo_items, dependent: :destroy
end
controllers have:
class TodoItemsController < ApplicationController
def index
#todo_list = TodoList.find(params[:todo_list_id])
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
end
def create
#todo_list = TodoList.find(params[:todo_list_id])
#todo_item = #todo_list.todo_items.new(params.require(:todo_item).permit(:description, :complete_at))
if #todo_item.save
redirect_to todo_list_todo_items_path(#todo_list)
end
end
end
index.html.erb
<div>
<div>
<% form_with(model: [#todo_list, #todo_item], local: true) do |f| %>
<% f.text_field :description %>
<% f.submit %>
<% end %>
</div>
<ul>
<% #todo_items.each do |todo_item| %>
<li><%= todo_item.description %></li>
<% end %>
</ul>
</div>
class TodoItemsController < ApplicationController
# use callbacks instead of repeating yourself
before_action :set_todolist, only: [:new, :create, :index]
def index
#todo_items = #todo_list.todo_items
#todo_item = TodoItem.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_list_params)
if #todo_item.save
redirect_to [#todo_list, :todo_items]
else
render :new
end
end
private
def set_todolist
#todo_list = TodoList.find(params[:todo_list_id])
end
# use a private method for your params whitelist for readibility
# it also lets you reuse it for the update action
def todo_list_params
params.require(:todo_item)
.permit(:description, :complete_at)
end
end
You where setting a different instance variable (#new_todo) in you index action. The polymorphic route helpers that look up the route helpers from [#todo_list, #todo_item] call compact on the array. So if #todo_item is nil its going to call todo_lists_path instead - ooops!
You alway also need to consider how you are going to respond to invalid data. Usually in Rails this means rendering the new view. If you are rendering the form in another view such as the index view it can get kind of tricky to re-render the same view as you have to set up all the same data as that action which leads to duplication.
It seems #new_todo has been added to #todo_items somehow in index action:
def index
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
# The above line has a side effect: #todo_items = #todo_items + [#new_todo]
end
I'm not sure it's a bug or feature from Rails (I use Rails 6.1.1).
For a quick fix, you can change #todo_list.todo_items.new to TodoItem.new.
I'm having a association between user, store and item like this:
user has one store
store has many items
item belongs to store
So when I create item it must belongs to the current user's store
Now, I logged in as user_1 , I want to search for user_2's item. But if I didn't create user_1's store, It keep redirecting me to localhost:3000/stores
in items_controller.rb:
class ItemsController < ApplicationController
before_action :find_item, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:new, :edit]
def index
if params[:category].blank?
#items = Item.all.order("created_at DESC")
end
if params[:category]
#category_id = Category.find_by(name: params[:category]).id
#items = Item.where(category_id: #category_id).order("created_at DESC")
end
end
def show
end
def new
#store = current_user.store
#item = #store.items.build
#categories = Category.all.map{ |c| [c.name, c.id] }
end
def update
#item.category_id = params[:category_id]
if #item.update(item_params)
redirect_to item_path(#item)
else
render 'edit'
end
end
def create
#store = current_user.store
#item = #store.items.build(item_params)
#item.category_id = params[:category_id]
if #item.save
flash[:success] = "Creating item success"
redirect_to #item
else
render 'new'
end
end
private
def item_params
params.require(:item).permit(:code, :name, :description, :producer, :item_img, :store_id,
:category_id )
end
def find_item
#item = Item.find(params[:id])
end
def find_user
#user = User.find_by(params[:user_id])
end
end
in stores_controller.rb:
class StoresController < ApplicationController
before_action :find_store, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :find_user
def show
if current_user.store.blank?
redirect_to new
else
#items = Item.where(store_id: #store.id)
end
end
def index
#stores = Store.all.order("created at DESC")
end
def new
#store = current_user.build_store
end
def create
#store = current_user.build_store(store_params)
if #store.save
session[:store_id] = #store.id
flash[:success] = "Creating item success"
redirect_to #store, notice: 'success'
else
render 'new'
end
end
private
def store_params
params.require(:store).permit( :name , :user_id)
end
def find_store
#store = Store.find(params[:id])
end
def find_user
#user = Store.find_by(params[:user_id])
end
end
The error raised whenever I click on the button in items/show.html.erb.
In items/show.html.erb:
<button>see more item from:<%= link_to #item.store.name, store_path(#item.store.id)%></button>
in routes.rb:
devise_for :users
resources :items
resources :stores
In the show method for stores_controller, I still want to load the current_user's store in navbar section to make sure he can add more items to his store when he logged in.
I'm still a newbie in rails and I'm looking for solutions to solve this problem :-)
If a store requires a user_id then you don't need before_action :find_user because you can get the store's user just by calling store.user
It looks like you require the current_user to have a store but what if they are not logged in? Shouldn't it be this?
def show
if current_user && current_user.store.blank?
redirect_to new
else
#items = Item.where(store_id: #store.id)
end
end
I'm a beginner to Ruby on Rails working on a Notebook app. I'm trying using Searchkick to enable users to quickly search their notes. I currently have 2 users (via devise gem).
I have just set up Searchkick, but when I search for a word that both the users have in their notes, the result shows notes by both users. So, a user can see the other's note in this case as in the image below.
Here is my notes_controller.rb code:
class NotesController < ApplicationController
before_action :find_note, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#notes = Note.where(user_id: current_user).order("created_at DESC")
end
def search
if params[:search].present?
#notes = Note.search(params[:search])
else
#notes = Note.all
end
end
def new
#note = current_user.notes.build
end
def create
#note = current_user.notes.build(note_params)
if #note.save
redirect_to root_path, notice: "Note successfully created."
else
render 'new'
end
end
def show
end
def edit
end
def update
if #note.update(note_params)
redirect_to note_path(#note), notice: "Note successfully updated."
else
render 'edit'
end
end
def destroy
#note.destroy
redirect_to root_path, notice: "Note successfully deleted."
end
private
def note_params
params.require(:note).permit(:title, :body)
end
def find_note
#note = Note.find(params[:id])
end
end
My routes.rb code:
Rails.application.routes.draw do
devise_for :users
resources :notes do
collection do
get :search
end
end
authenticated :user do
root "notes#index"
end
root "welcome#home"
end
My search.html.erb code, which is the same as index.html.erb code:
<% #notes.each do |note| %>
<h2><%= link_to note.title, note_path(note) %></h2>
<% end %>
I have a feeling I need to add a conditional statement in the search action in the notes_controller but that is not working.
Can anyone help please?
Thank you.
That would be
#notes = Note.search params[:search], where: { user_id: current_user.id }
I am building a fairly straightforward Rails 5 application. You create "Movies," and then can create "Reviews" for those movies. Rails is doing something odd. I have my application set up so instead of a "new" action and a corresponding view, I have the form to create new movies in a modal contained in the application.html.erb file. I then provide #newmovie = Movie.new in the controller for all the movie views, so the data is available everywhere.
I have validations for the movie object setup like this:
class Movie < ApplicationRecord
has_many :reviews
validates :title, :director, :poster, :synopsis, presence: true
end
When I fill out the form to create a new movie in the modal on my index view and intentionally leave fields blank (to trigger the validation), I get this:
instead of the form simply not accepting the input. What's going on here? I can't have this error happening like this. Previously, I had a "new" view. This did not happen in the previous setup. Help!
Here is my entire movies controller:
class MoviesController < ApplicationController
before_action :find_movie, only: [:show, :edit, :update, :destroy ]
before_action :authenticate_user!, only: [:new, :create, :edit, :update, :destroy]
before_action :find_newmovie, only: [:show, :index, :new, :edit]
def show
#reviews = #movie.reviews.all.order(created_at: :desc).paginate(:page => params[:page], :per_page => 3)
#review = Review.new
if #movie.reviews.blank?
#average_review = 0
else
#average_review = #movie.reviews.average(:rating).round(2)
end
end
def index
#movies = Movie.all.order(title: :asc).paginate(:page => params[:page], :per_page => 3)
#newmovie = Movie.new
end
def new
#movie = Movie.new
end
def create
#movie = Movie.create(movie_params)
#movie.user_id = current_user.id
if #movie.save
flash[:success] = "Your movie was created!"
redirect_to root_path
else
flash[:danger] = "There was a problem with your request"
render :new
end
end
def edit
end
def update
if #movie.update(movie_params)
flash[:success] = "Your movie was updated"
redirect_to movie_path
else
flash[:danger] = "There was a problem with your request"
end
end
def destroy
if #movie.destroy
flash[:success] = "Your movie was removed"
redirect_to movies_path
else
flash[:danger] = "There was a problem with your request"
render :index
end
end
private
def movie_params
params.require(:movie).permit(:title, :director, :poster, :synopsis, :user_id)
end
def find_movie
#movie = Movie.find(params[:id])
end
def find_newmovie
#newmovie = Movie.new
end
end
In new action of movies_controller, you missed to initalize the instance variable #newmovie.
add below code in movies_controller:
class MoviesController < ApplicationController
def new
#newmovie = Movie.new
end
...
end
So I am in the process of setting up a forum and everything is setup/working well except for my replies are not appearing on the thread "show" page. After checking the rails console, I see they are saving but the user_id and discussion_id are no. The user_id is always nil and the discussion_id is always 0. The discussion threads were easier to setup but with having these replies, I obviously seem to be having an issue. Here are my snippets of code:
class PostsController
# ...
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def create
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
params.require(:post).permit(:reply)
end
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
class DiscussionsController
def show
#discussion = Discussion.friendly.find(params[:id])
#post = Post.new
render :layout => 'discussion'
end
end
Partial rendered to reply:
<h2>Reply</h2>
<%= form_for [ #discussion, #post ] do |f| %>
<p>
<%= f.label :reply, "Reply" %><br/>
<%= f.text_field :reply %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
Partial rendered to show replies in on discussion page:
<h3><%= post.user.first_name %></h3>
<%= post.reply %>
Posted: <%= post.created_at.strftime("%b. %d %Y") %></p>
<p><%= link_to "Delete Comment", [post.discussion, post], data: {confirm: "Are you sure you wish to delete?"}, method: :delete, :class => "post_choices" %></p>
Just want to mention that I also have the correct associations between the three models (User, Discussion, Post). If there is more code needed, please let me know. I appreciate it very much for any information that may be helpful =)
Joe
EDIT
class User < ActiveRecord::Base
has_many :articles
has_many :discussions
has_many :posts
# ...
end
class Discussion
belongs_to :user
has_many :posts
extend FriendlyId
friendly_id :subject, use: :slugged
end
class Post
belongs_to :discussion
belongs_to :user
end
I could post the entire user model if needed but its all validations/devise aspects =P The other two I listed all of the contents in the models.
Edit 2
Thanks to Max, the user_id returns correctly in the console but still not the discussions. Going go dig around a bit more with the recent changes to see what else =)
There are a few issue you need to deal with.
First you should ensure that Devise is actually authorizing your controller action.
class PostsController < ApplicationController
before_filter :authenticate_user!
end
Otherwise current_user will return nil if there is no signed in user. And I'm
guessing that you do not want un-authenticated users to be able to create posts.
Also if you have a nested route you most likely want to check that the discussion actually
exists before trying to add posts.
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
private
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
When you are creating resources be careful not to query the database needlessly.
This especially applies to CREATE and UPDATE queries which are expensive.
def create
#post = Post.create(post_params) # INSERT INTO 'users'
#post.discussion_id = params[:discussion_id]
#post.user = current_user
#post.save # UPDATE 'users'
flash.notice = "It has been posted!"
redirect_to discussions_path(#post.discussion)
end
Also you are not even checking if the record was created successfully.
So lets put it all together:
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def new
#post = #discussion.post.new
end
def create
# new does not insert the record into the database
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new # or redirect back
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
# Only permit the params which the user should actually send!
params.require(:post).permit(:reply)
end
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end