A RoR beginner here. I am trying to implement a page 'My Products' to show all products added by current user. Currently I have set my 'All Products' page as my index root "products#index". My Product has Controller but my User does not not Controller because I am using Devise. I have a few questions which I still couldnt get it right.
Because currently
Product
belongs_to :user
User
has_many :products
routes
devise_for :users
resources :products
rake routes I have
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel
user_registration POST /users(.:format) devise/registrations#create
new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit
PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
products GET /products(.:format) products#index
POST /products(.:format) products#create
new_product GET /products/new(.:format) products#new
edit_product GET /products/:id/edit(.:format) products#edit
product GET /products/:id(.:format) products#show
PATCH /products/:id(.:format) products#update
PUT /products/:id(.:format) products#update
DELETE /products/:id(.:format) products#destroy
Currently what I have in mind to achieve this:
Create a UserController to have #user = User.all then because with association I can use #user.product. (I saw some people said that I shouldn't create new controller because of Devise?) However, I tried to test with rails console before doing the implementation.
product = Product.first
product.user #here I get all details about user
user = User.first
user.product #here I get error. Why it works for product to find user but not both ways?
Next, this then makes me wonder how do I create configure the route to get path /users/:id/products . Should I use a nested resource?
Next question, I tried to have
resources :users do
resources :products
end
resources :products
I typed in rake routes again. I am confused
user_products GET /users/:user_id/products(.:format) products#index
products GET /products(.:format) products#index
Question:
Can 2 path has the same Controller#Action?
user_products_path and products_path are 2 different path where user_products_path represents user's product. and products_path represents all products
Sorry for the long post.I really wish to clarify those things. Thanks a lot if you could help!! :)
user.product #here I get error. Why it works for product to find user but not both ways?
As User has_many :products, you need to use correct association name as:
user.products # it will give you all products of an user
Can 2 path has the same Controller#Action?
In short, YES. You can specify controller and action name explicitly in your route.rb like:
get 'users/:id/products' => 'products#index', :as => :user_products_path
Note that in your case, it will be going into products#index by default also.
user_products_path and products_path are 2 different path where user_products_path represents user's product. and products_path represents all products
Correct, it's absolutely fine. As you are using devise, you don't need to use id parameter coming from the URL i.e. users/:id/products. You can get the user directly from current_user, which is better in terms of security also. Keep in mind that if you have admin role who can see details of every user then you will need to manage id in that case.
Because you're just starting out, I'll keep this example simple with having easy to read logic. However, there are better ways to configure and optimize this so that you're not relying on if statements in your controller.
class ProductsController < ApplicationController
def index
if params[:user_id]
#user = User.find(params[:user_id])
#products = #user.products.all
else
#products = Product.all
end
end
end
One reason why this is bad practice is that you're relying on the user_id param to be valid. If the user_id param is not valid, then the second line #products = #user.products.all will return undefined method for Nil class.
Also, in your views, if you reference #user then you will get an error if you're just listing out all products.
Also, there isn't any security being done where anyone can see anyone's products if they know or try to guess the user's id.
However, this should work in your instance. You may also want to consider that if the user_id is present to then call current_user.products.all so that it will only return the current user's products instead of the particular user_id's. However, it is really up to how you're designing the application that will determine the best route to take.
What I'll typically do is if the /products route should be for the administrative level, I'll namespace it and put the appropriate security around the namespacing so that it does not allow general users to access it, but rather only administrators of the site.
Related
I have a Tool model and I want to be able to have both a regular tools index (tools_path) that lists all the tools in the database for all users & also an users tool index(users_tools_path) that lists all the tools for a particular user.
I am not sure what the rails way of implementing this is. I am using devise and my instinct was to do the following in my routes:
Rails.application.routes.draw do
devise_for :users
root 'tools#index'
resources :users do
resources :tools
end
resources :tools
end
This gets me the following routes:
Prefix Verb URI Pattern Controller#Action
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel
user_registration POST /users(.:format) devise/registrations#create
new_user_registration GET /users/sign_up(.:format) devise/registrations#new
edit_user_registration GET /users/edit(.:format) devise/registrations#edit
PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
root GET / tools#index
user_tools GET /users/:user_id/tools(.:format) tools#index
POST /users/:user_id/tools(.:format) tools#create
new_user_tool GET /users/:user_id/tools/new(.:format) tools#new
edit_user_tool GET /users/:user_id/tools/:id/edit(.:format) tools#edit
user_tool GET /users/:user_id/tools/:id(.:format) tools#show
PATCH /users/:user_id/tools/:id(.:format) tools#update
PUT /users/:user_id/tools/:id(.:format) tools#update
DELETE /users/:user_id/tools/:id(.:format) tools#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
tools GET /tools(.:format) tools#index
POST /tools(.:format) tools#create
new_tool GET /tools/new(.:format) tools#new
edit_tool GET /tools/:id/edit(.:format) tools#edit
tool GET /tools/:id(.:format) tools#show
PATCH /tools/:id(.:format) tools#update
PUT /tools/:id(.:format) tools#update
DELETE /tools/:id(.:format) tools#destroy
This is my tools controller:
class ToolsController < ApplicationController
before_action :set_tool, only:[:show, :edit, :update, :destroy]
before_action :authenticate_user!, only:[:new, :destroy, :edit], notice: 'you must be logged in to proceed'
def index
#tools = Tool.all
end
def show
end
def new
#user = current_user
#tool = #user.tools.build
end
def create
#tool = Tool.new(tool_params)
#tool.save
redirect_to #tool
end
def edit
end
def update
#tool.update(tool_params)
redirect_to #tool
end
def destroy
#tool.destroy
redirect_to tools_path
end
private
def set_tool
#tool = Tool.find(params[:id])
end
def tool_params
params.require(:tool).permit(:name, :description)
end
end
These are my models:
class Tool < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :tools
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Unfortunately at this point when I navigate to users/id/tools all the tools in the database are listed instead of only the tools for that particular user. Also when I look at the active record instances of each tool in my database the user_id column is nil
However I am stuck here. Obviously the code will be different for each index so I can't use the same index action for both situations or the same index.html.erb view page either.
I am wondering wheat the next step would be?
Here are a few things I am thinking I could do:
1) create a new action in my tools controller something like user_index and include the user & tool logic to fetch the tools associated to a particular user. I would also have to create a new user_index.html.erb view with the view code. Then I would delete the nested resource and add a route like: match 'users/:id/tools' => 'tools#user_index', :via => get
2) I can delete the stand alone resources :tools route and the add a get :user_index, :on => :collection in my nested tool resource. Then add a user_index action to my controller like in solution #1. The only problem here is that then all my routes would have the user/:id prefix which I do not want.
What is the best solution in this case? Also, why are my active record tool instances not saving the id of the user that created them? Is there a way to get them to save the user id without nesting the resources?
What is the rails way for this?
To only show the tools of a particular user, instead of doing:
#tools = Tool.all
You can make an instance variable that is an array of Tools that are assigned to a particular user. For example:
#user = User.find(params[:id])
#tools = Tool.where(user_id: #user.id)
This will only collect the tools where the tools user_id matches the user ID of the user loaded into the show view.
Then when you do your loop, it will show the right tools for that user.
With the answer above you can use:
#tools = (#user.present?) ? #user.tools : Tool.all
To be able to set the user of each tool you have to edit your:``
params.require(:tool).permit(:name, :description)
To include user
params.require(:tool).permit(:name, :description, :user)
I know this question has been asked quite a few times, but somehow it's not working for me. I would like the user to be redirected to a specific page after log in (using Devise). I'm using Rails 3.2 and Ruby 4.
As it is the user get redirected to:
- After login they get redirected to the root, if the user got to the login page from one of the pages controlled by the controller of which the root path is part of. These pages do not require authorization.
- After login they get redirected to the requested page, if the user automatically got to the login page by clicking on a page that requires authorization.
To redirect the user after login to a specific page, I understand that you need to add to the controller some lines. To the controller that is controlling the pages that required authorization I added:
class AccountsController < ApplicationController
before_filter :authenticate_user!
def user1_home
end
def user2_home
end
def user3_home
end
protected
def after_sign_in_path_for(resource)
'accounts/user1_home'
end
end
However, this didn't change anything. I also tried it with def after_sign_in_path_for(resource) 'accounts#user1_home_path' end
Should I perhaps also change something in routes.rb?
My routes are:
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel
user_registration POST /users(.:format) devise/registrations#create
new_user_registration GET /users/sign_up(.:format) devise/registrations#new
edit_user_registration GET /users/edit(.:format) devise/registrations#edit
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
user_confirmation POST /users/confirmation(.:format) devise/confirmations#create
new_user_confirmation GET /users/confirmation/new(.:format) devise/confirmations#new
GET /users/confirmation(.:format) devise/confirmations#show
toc GET /toc(.:format) pages#toc
stakeholder GET /stakeholder(.:format) pages#stakeholder
about GET /about(.:format) pages#about
doelgroep_onderneming GET /doelgroep_onderneming(.:format) pages#doelgroep_onderneming
doelgroep_ngo GET /doelgroep_ngo(.:format) pages#doelgroep_ngo
doelgroep_se GET /doelgroep_se(.:format) pages#doelgroep_se
partnership GET /partnership(.:format) pages#partnership
contact GET /contact(.:format) pages#contact
account_stakeholder GET /account/stakeholder(.:format) accounts#stakeholder_home
account_business GET /account/business(.:format) accounts#business_home
account_admin GET /account/admin(.:format) accounts#admin_home
root / pages#index
You definitely shouldn't be defining methods per user in the Accounts controller.
Devise has instructions on github for accomplishing this: https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in
You need to move after_sign_in_path_for to the ApplicationController class and you shouldn't have 'accounts/user1_home'. To go to the right url for each use you should have something along the lines of user_path(#user) but I don't have enough of your code to know exactly what to use. You should run in a console window the rake routes command to see what you routes you have made available. Read more about routing at: http://guides.rubyonrails.org/routing.html
Yes. Set the following at any point (before or after login):
session["user_return_to"] = request.original_url # Will redirect the user to where they came from
If it's an authenticated page they tried to access before logging in you want to redirect to then you can simply do it this way in your ApplicationController:
def after_sign_in_path_for(resource)
if session[:user_return_to] == nil
your_actual_next_path
else
super
end
end
I'm trying to get to grips with Rails and I'm stuck on getting an 8 question multi-choice question quiz working.
I think I understand the MVC side of things. Requests from the browser come through a controller. It asks for info from models, sends it to the views and returns the content to the browser (please correct me if I'm wrong).
I DON'T understand (very clearly, at least) how pages 'know' where to link to. I think, when you generate a resource, you're given a plethora of routes mapped to paths. I don't understand when those paths are relevant.
Could someone please explain to me (step-by-step, if necessary) how the submit button in a Devise form 'knows' where to link to?
My form, so far, looks like this:
<%= form_for([current_user]) do |f| %>
<p>
<%= f.check_box :quiz_answers %>
</p>
<p>
<%= f.submit("Get my results!") %>
</p>
<% end %>
I see nothing in there that NAMES a path or a route. How, then, is the link generated?
I have checked rake routes, but I don't think I'm appreciating what is significant about that list. If someone could explain the MEANING behind it, that would be great.
I will try to explain it.
There are several types of methods that you can send through web page:
-GET
-POST
-DELETE
-PUT
Get sends parameters through link. you can see its parameters in URL, for instance www.google.ba?you_are=user, you_are is parameter and its value is user.
Post sends parameters through package so you can't see anything in the url, yet informations are there.
Other methods are variations for POST method for restful service.
For your question, devise creates by default some routes that are not clearly visible in routes.rb. Any request that comes across your webpage goes to the routes.rb, checks where should it be delegated then send that to corresponding controller. Controller then takes parameters do some logic(delegates it to services and other things), prepares models for show and then delegates it to template. Then, template with all informations(rendered) is sent to web browser.
There is one thing to mention, resource or devise creates default actions for every method for corresponding action. In this case you just need to set model and it knows where to delegates it.
Here are my routes for devise ( some of them are custom):
new_user_session GET /users/sign_in(.:format) user_sessions#new
user_session POST /users/sign_in(.:format) user_sessions#create
destroy_user_session DELETE /users/sign_out(.:format) user_sessions#destroy
user_password POST /users/password(.:format) passwords#create
new_user_password GET /users/password/new(.:format) passwords#new
edit_user_password GET /users/password/edit(.:format) passwords#edit
PATCH /users/password(.:format) passwords#update
PUT /users/password(.:format) passwords#update
cancel_user_registration GET /users/cancel(.:format) users#cancel
user_registration POST /users(.:format) users#create
new_user_registration GET /users/sign_up(.:format) users#new
edit_user_registration GET /users/edit(.:format) users#edit
PATCH /users(.:format) users#update
PUT /users(.:format) users#update
DELETE /users(.:format) users#destroy
user_confirmation POST /users/confirmation(.:format) confirms#create
new_user_confirmation GET /users/confirmation/new(.:format) confirms#new
GET /users/confirmation(.:format) confirms#show
Use rake routes command in terminal to find out all your routes.
I have a resource called "Store" that can have many accounts registered, but how im supposed to do this using device? i've setup this
class Store
has_many :accounts
end
class Account
belongs_to :store
end
Panel::Application.routes.draw do
resources :stores do
devise_for :accounts
end
end
but im getting
Showing panel/app/views/accounts/registrations/new.html.haml where line #2 raised:
undefined method `account_registration_path' for #<Module:0x007f9a67940918>
This is bc its expecting something like "account_store_registration_path" but where all this logic goes
rake routes output
account_store_password POST /accounts/stores/:store_id/password(.:format) devise/passwords#create
new_account_store_password GET /accounts/stores/:store_id/password/new(.:format) devise/passwords#new
edit_account_store_password GET /accounts/stores/:store_id/password/edit(.:format) devise/passwords#edit
PATCH /accounts/stores/:store_id/password(.:format) devise/passwords#update
PUT /accounts/stores/:store_id/password(.:format) devise/passwords#update
cancel_account_store_registration GET /accounts/stores/:store_id/cancel(.:format) devise/registrations#cancel
account_store_registration POST /accounts/stores/:store_id(.:format) devise/registrations#create
new_account_store_registration GET /accounts/stores/:store_id/sign_up(.:format) devise/registrations#new
edit_account_store_registration GET /accounts/stores/:store_id/edit(.:format) devise/registrations#edit
PATCH /accounts/stores/:store_id(.:format) devise/registrations#update
PUT /accounts/stores/:store_id(.:format) devise/registrations#update
DELETE /accounts/stores/:store_id(.:format) devise/registrations#destroy
account_store_confirmation POST /accounts/stores/:store_id/confirmation(.:format) devise/confirmations#create
new_account_store_confirmation GET /accounts/stores/:store_id/confirmation/new(.:format) devise/confirmations#new
GET /accounts/stores/:store_id/confirmation(.:format) devise/confirmations#show
So to accomplish this i did something very simple
I define an additional route for the account:
Panel::Application.routes.draw do
devise_for :accounts
resources :stores do
resources :accounts
end
end
I generated an accounts controller and views, and used the Devise generated model. So I used the new controller to create accounts and the devise helpers to manage the login logic.
So now i can have Accounts that belongs to an store
I am using the devise gem to handle all the sign up and sign in stuff. But I also want to add user profiles to my application, so I generated a user controller with only a show action. Then I added get 'users/:id' => 'users#show' to routes.rb. In fact, typing /users/1 works, but I can't find a way to name the route. What I want is to get something like show_user_path or user_path so I can link to a given user's content and show that user's profile.
Here is my routes.rb
Pinteresting::Application.routes.draw do
resources :pins
get 'users/:id' => 'users#show'
devise_for :users
root "pins#index"
get "about" => "pages#about"
And here are the routes I get with it (I highlighted the one I expect to be something like show_user_path):
pins_path GET /pins(.:format) pins#index
POST /pins(.:format) pins#create
new_pin_path GET /pins/new(.:format) pins#new
edit_pin_path GET /pins/:id/edit(.:format) pins#edit
pin_path GET /pins/:id(.:format) pins#show
PATCH /pins/:id(.:format) pins#update
PUT /pins/:id(.:format) pins#update
DELETE /pins/:id(.:format) pins#destroy
#this is the one I want a path! GET /users/:id(.:format) users#show
new_user_session_path GET /users/sign_in(.:format) devise/sessions#new
user_session_path POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session_path DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password_path POST /users/password(.:format) devise/passwords#create
new_user_password_path GET /users/password/new(.:format) devise/passwords#new
edit_user_password_path GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration_path GET /users/cancel(.:format) devise/registrations#cancel
user_registration_path POST /users(.:format) devise/registrations#create
new_user_registration_path GET /users/sign_up(.:format) devise/registrations#new
edit_user_registration_path GET /users/edit(.:format) devise/registrations#edit
PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
root_path GET / pins#index
about_path GET /about(.:format) pages#about
For devise, User is not the resource, it's just a scope. What devise cares about is authentication.
Although the paths are nested under /user, you will notice that the resources that are defined are actually things like sessions, registrations, passwords...
Just add resources :users in your routes and create a UsersController (and the views).
If you don't want to create all the resources for users and just be able to user user_path(user) with your get 'users/:id, you can name that route using the as option, like this:
get 'users/:id' => 'users#show', as: user
The answer above is great but I think it's worth noting here that if you don't want to create a different controller, but want to add an action to, say, your registrations controller that you inherit from the Devise::RegistrationsController, then you need to use the devise scope block:
devise_scope :user do
get 'users/:id' => 'registrations#show', as: user
end
the answer supposed to be like this:
get 'user/:id' => 'users#show', as: :user