I've got a one to many relationship with one movie per list entry, where a movie can be used in a list entry. My list_entries table has a movie_id and a list_id.
database schema
I've nested list_entries in lists so I can pass the list_id directly when creating a new instance.
Rails.application.routes.draw do
devise_for :users
root to: 'pages#home'
resources :movies, only: [:new, :index, :create, :show, :destroy, :edit, :update]
resources :users, only: [:show, :edit, :update]
resources :lists, only: [:new, :create, :show, :index, :destroy] do
resources :list_entries
end
end
Right now I can create and destroy list entries but I have to specify the movie id manually.
The UX I want to achieve is for the user to be able to search for movies from themy list_entries/new form but I don't even know where to begin.
The form as it is now:
<%= simple_form_for #list_entry, url: list_list_entries_path do |f| %>
<%= f.input :comment %>
<%= f.input :movie_id %>
<%= f.submit "Add", class: "btn devise-button" %>
<% end %>
My list entries controller:
class ListEntriesController < ApplicationController
before_action :find_list, only: [:index, :create, :show, :new, :destroy]
def new
#list_entry = ListEntry.new
end
def index
#list_entries = ListEntry.where(list: #list)
end
def show
#list_entry = ListEntry.find(params[:id])
end
def destroy
#list_entry = ListEntry.find(params[:id])
#list_entry.destroy
redirect_to list_list_entries_path
end
def create
#list_entry = ListEntry.new(entry_params)
#list_entry.list_id = params[:list_id]
if #list_entry.save
redirect_to list_list_entries_path
else
render 'new'
end
end
private
def find_list
#list = List.find(params[:list_id])
end
def entry_params
params.require(:list_entry).permit(:comment, :movie_id)
end
end
If you don't want to manually specify a movie_id in the form, you can use the simple_form_for association helper:
<%= f.input :comment %>
<%= f.association :movie %>
I believe it should be labeled based off of the movie title, but if not, you may have to specify a #to_label method in your Movie model.
Alternatively, you could query for the movies in your #new action and use them to do whatever you like in your view:
def new
#list_entry = ListEntry.new
#movies = Movie.all # or whatever query you think is relevant
end
The #collection_select documentation might be useful here:
https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select
Related
I have a question about many-to-many associations in Ruby on Rails.
I have 3 models in my app : Topic, Meeting and Todo associated with a manu-to-many association.
class Todo < ApplicationRecord
belongs_to :topic
belongs_to :meeting
end
then
class Meeting < ApplicationRecord
has_many :todos
end
and
class Topic < ApplicationRecord
has_many :todos
end
I made my routes and controller to be able to create new todos via a meeting :
Rails.application.routes.draw do
resources :meetings, only: [:index, :show, :new, :create, :edit, :update] do
resources :todos, only: [:index, :new, :create]
end
resources :todos, only: [:index, :show, :edit, :update, :destroy]
end
and
class TodosController < ApplicationController
def new
#topic = Topic.find(params[:topic_id])
#todo = Todo.new
end
def create
#todo = Todo.new(todo_params)
#meeting = Meeting.find(params[:meeting_id])
#todo.meeting = #meeting
#todo.save
redirect_to meeting_path(#meeting)
end
private
def todo_params
params.require(:todo).permit(:topic_id, :meeting_id, :note, :deadline, :title)
end
end
and my view :
<h3><%= #meeting.date %></h3>
<%= simple_form_for [#meeting, #todo] do |f| %>
<%= f.input :title %>
<%= f.input :note %>
<%= f.date_field :deadline %>
<%= f.association :topic, label_method: :nom, value_method: :id %>
<%= f.submit "Add a todo" %>
<% end %>
My problem is that I want to be able to create todo via topics aswell and when I add my routes :
resources :topics, only: [:index, :show, :new, :create] do
resources :todos, only: [:index, :new, :create]
end
When I tried to complete my controller and test it, it seems to be tricky. If I add:
#topic = Topic.find(params[:topic_id])
Then it tells me that it needs a meeting...
Any idea ?
You can create the different routes with:
resources :meetings do
resources :todos, only: [:index, :new, :create]
end
resources :topics do
resources :todos, only: [:index, :new, :create]
end
You can avoid duplication by using routing concerns:
concerns :todoable do
resources :todos, only: [:index, :new, :create]
end
resources :topics, concerns: :todoable
resources :meetings, concerns: :todoable
In your controller you can check for the presences of the meeting_id or topic_id parameters:
class TodosController < ApplicationController
before_action :set_parent
def new
#todo = Todo.new
end
def create
#todo = #parent.todos.new(todo_params)
if #todo.save
redirect_to #parent
else
render :new
end
end
private
def parent_class
#parent_class ||= if params[:meeting_id].present?
Meeting
else if params[:topic_id].present?
Topic
else
# raise an error?
end
end
def set_parent
id = params["#{parent_class.model_name.param_key}_id"]
#parent = parent_class.find(id)
end
def todo_params
params.require(:todo)
.permit(:topic_id, :meeting_id, :note, :deadline, :title)
end
end
<%= simple_form_for [#parent, #todo] do |f| %>
<%= f.input :title %>
<%= f.input :note %>
<%= f.date_field :deadline %>
<%= f.association :topic, label_method: :nom, value_method: :id if #parent.is_a?(Meeting) %>
<%= f.association :meeting, label_method: :nom, value_method: :id if #parent.is_a?(Topic) %>
<%= f.submit "Add a todo" %>
<% end %>
Any help and advise would be much appreciated.
i have a company model that has many recruiters (users) &
a recruiter (user) belonging to a company
also recruiters belong to a gender & gender has many recruiters
using the GEM ransack, i am trying to display all male recruiters(users) in a
company but i am unsure of the code needed for the 'recruiters' action in the companies controller - anyway help & advise would be much appreciated. Below are my files:
Models:
user.rb
class User < ActiveRecord::Base
belongs_to :company
belongs_to :category_gender
end
company.rb
class Company < ActiveRecord::Base
has_many :users
end
category_gender
class CategoryGender < ActiveRecord::Base
has_many :users
end
Companies_controller.rb
class CompaniesController < ApplicationController
respond_to :html, :xml, :json
before_action :set_company, only: [:show, :edit, :update, :destroy]
# load_and_authorize_resource
........
def recruiters
#search = Company.search(params[:q])
#companies = #search.result.includes(:users)
#user = current_user
#company = #user.company
end
end
routes.rb
Rails.application.routes.draw do
resources :companies do
resources :comments, only: [:create, :destroy]
member do
get 'recruiters'
end
end
devise_for :users
resources :users do
resources :comments, only: [:create, :destroy]
resources :adverts
resources :blogs
member do
get 'applications'
get 'applicants'
get 'settings'
get 'dashboard'
get 'management'
end
end
recruiters.html.erb (views file):
<%= search_form_for #search, url: recruiters_company_path do |f| %>
<div>
<h3>gender</i></h3>
<ul>
<% CategoryGender.all.each do |gender| %>
<li>
<%= check_box_tag('q[category_gender_name_eq_any][]', gender.name) %>
<%= gender.name %> (<%= gender.users.count %>)
</li>
<% end %>
</ul>
</div>
<button><%= f.submit 'search' %></button>
<% end %>
Since you are searching for Users. Then, .ransack method should be called on User model and not on Company model.
Try the following in your recruiters method:
def recruiters
#company = Company.find(params[:id])
#users = User.ransack(params[:q]).result.where(company: #company)
#user = current_user
end
1st line: I used #company = Company.find(params[:id]) instead of #company = #user.company because you are using resources pathing for Company, in which recruiter method is a "member" of (as you defined in your routes.rb).
The problem with #user.company is that say for example a user browses /companies/1/recruiters, the #company that will be assigned is going to be the same as when browsing /companies/3/recruiters. But by convention, #company in these two paths should already be different because of the id changing.
2nd line: I just added a the predicate .where(company: #company) so that the results will be filtered to the respective company.
Then in your recruiters.html.erb view file, you could just add something like the following:
...
...
<% #users.each do |user| %>
<%= user.name %>
<% end %>
...
...
I'm in the process of building an edit form. I have completed the form and it renders as it should. When I go to submit the update to the form I get a no route error. The path to my edit page is for example '/topics/1/bookmarks/1/edit'. This page loads perfectly fine. That page contains a partial of the form that will be used to edit the record. When I select the submit button however it re-routes to '/topics/1/bookmarks/1' and gives me the following:
Routing Error
No route matches [PATCH] "/topics/1/bookmarks/1"
Below are the files that should be of importance let me know if there is something I did not share. That would be important to view.
bookmarks_controller.rb
def edit
#topic = Topic.find(params[:topic_id])
#bookmark = Bookmark.find(params[:id])
end
def update
#topic = Topic.find(params[:topic_id])
#bookmark = Bookmark.find(params[:id])
if #bookmark.update_attributes(params.require(:bookmark).permit(:url, :topic_id, :description))
flash[:notice] = "Bookmark was updated"
redirect_to [#topic, #bookmark]
else
flash[:error] = "There was an error saving the Bookmark. Please try again."
render :edit
end
end
config/routes.rb
resources :topics do
resources :bookmarks, only: [:show, :new, :edit]
end
bookmarks/_form.html.erb
<%= form_for [topic, bookmark] do |f| %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :url %>
<%= f.text_field :url %>
<%= f.submit %>
<% end %>
bookmarks/edit.html.erb
<%= render partial: 'form', locals: {topic: #topic, bookmark: #bookmark} %>
You don't have an update route, which is what actually updates the database. Just change
resources :bookmarks, only: [:show, :new, :edit]
to
resources :bookmarks, only: [:show, :new, :edit, :update]
OR better yet,
resources :bookmarks, except: [:index, :create, :destroy]
If you have a new action, then you should want a create action too. So, finally:
resources :bookmarks, except: [:index, :destroy]
Im getting this weird error when i want to update my model (model name carts).
Error : The action 'update' could not be found for CartsController
this is my carts_controller.rb :
class CartsController < ApplicationController
include CartForcable
before_action :scoped_cart, only: [:show, :update]
def show
end
private
def scoped_cart
force_cart! lambda {|r| r.includes(:entries => {:sign => [:dimensions, :substrates]})}
end
def update
#cart = #cart.find(params[:id])
if #cart.update_attributes(cart_params)
flash[:notice] = translate 'flash.notice'
else
flash[:error] = translate 'flash.error'
end
support_ajax_flashes!
respond_to do |format|
format.html # renders view
format.json { render json: #entry }
end
end
end
and these are my routes.rb :
resources :categories, only: [:index] do
resources :signs, shallow: true, only: [:index]
end
resources :carts, only: [:show, :update]#, param: :cart_permalink
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
resources :cart_entries, only: [:index, :create, :update, :destroy], as: 'entries', path: 'entries'
end
resource :user, only: [:edit, :show, :update], as: 'current_user', path: 'profile'
resources :signs, only: [:show]
resources :pages, only: [:show], param: :permalink
and my show.html.erb has this form :
<%= form_for #cart, :url => {:controller => "carts", :action => "update" } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :regnum %>
<%= f.text_field :regnum %></br>
<%= button_to "Order/update", {:controller => "carts",:action =>
'update', :id => #cart.id }, :method => :update %>
<% end %>
After i enter name or number (for example), and click update button or something, it doesnt upload any data into model carts(which has the right columns).
Thanks,
Michael
Your update method is private; controller actions need to be public. Move
def update
#cart = Cart.find(params[:id])
...
end
to be above the
private
line. You need to do the find on the Cart model, not a #cart instance.
You can use a show action although edit would be more standard and fits right in with the RESTful routes without the need to override convention. The Rails routing guide should help here: http://guides.rubyonrails.org/routing.html
Additionally, you need to define the value of #cart in your edit action:
def edit
#cart = Cart.find(params[:id])
end
This will ensure it has a value on the edit form and so comes back into your update action through the parameters.
So if you try to edit a cart with something like /carts/12345/edit (where 12345 is the id of the cart you want to update) it should all hang together.
This looks wrong:
#cart = #cart.find...
don't you mean...?:
#cart = Cart.find...
And it seems you are using "cart_permalink" instead of "id" on your routes
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
Check the server log and see the name of the parameter that holds the ID, also run "bundle exec rake routes" to double check.
I am new to rails and am trying create a forum. The forum has many topics, topics belong to a forum and have many microposts, and microposts belong to both topics and users. However, no matter what I try, the posts will not be created. Currently when I try to post, I get the routing error "No route matches [GET] "/topics""
My routes.rb file:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy]
resources :forums, only: [:index, :show]
resources :topics, only: [:show]
_micropost_form.html.erb
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.hidden_field :topic_id, value: #topic.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.text_field :summary, placeholder: "One-line summary..." %>
<%= f.text_area :content, placeholder: "Compose a new post..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
microposts_controller.rb
class MicropostsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
##topic = Topic.find_by_id(params[:topic_id])
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Your solution has been posted!"
redirect_to topic_path(#topic)
else
redirect_to topic_path(#topic)
end
end
def destroy
#micropost.destroy
redirect_to root_url
end
private
def micropost_params
params.require(:micropost).permit(:summary, :content, :user_id)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end
end
As you can see, I commented out the first line in my create function because I've tried posting based on the the micropost's relationship to the topic to no avail. Thanks in advance and let me know if it would help if I posted more code!
In your :topics resource, you didn't defined the index method, that's why you won't be able to get to topic's list or index page. Try to change your route like this:
resources :topics, only: [:index, :show]
or remove only attribute from resources, it will automatically include all your methods by default.
resources :topics
Also if you have relationship between models, you should define nested routes in your routes
file, For example, you can define them like this, you can change them accordingly:
try to change your route file like this:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :forums do
resources :topics do
resources :microposts, only: [:new, :create, :destroy]
end
end
In above case, you can access your forums like this:
http://localhost:3000/forums
you can access your topics like this:
http://localhost:3000/forums/id/topics
you can access your microposts like this:
http://localhost:3000/forums/id/topics/id/microposts
If you want to access /microposts directly you have to put it outside any resource.
resources :microposts, only: [:index]
now you will be able to access it:
http://localhost:3000/microposts
Hope it will help. Thanks.