after we create an new entry the standard redirect is everytime to the :id. how can i change it to an own_key.
We have designed our routes
resources :lists
match '/:own_key' => 'lists/show'
If we create an new entry the redirect go to /list/:id
Is there a way to change it to redirect to own_key?
def create
#list = List.new(params[:list])
#list = List.create!(params[:list])
respond_with(#list)
end
Each model has a method called to_param which you can override
list.rb
class Bar < ActiveRecord::Base
def to_param
"#{own_key}.parameterize"
end
end
This will make that the path changes from /:id, to /:own_key.
NOTE: This will change only the value of the parameter but not the name of the parameter.
Example: If you want to acces with an own_key value of foo the show action /lists/foo there will not be a params[:own_key]. You can access the own_key value by with params[:id] but not with params[:own_key]
Parameters: {"id"=>"foo"}
Related
I feel like this should be super simple but it's not working as I thought it should.
I need to be able to have the new & create method of a controller allowed for a new object, based on the parent object being correct. Meaning the reference to the parent must be there and you must have the parent objects user id set correctly.
So given a couple objects like this;
class Resource < ApplicationRecord
belongs_to :user
has_many :resource_addendums
end
class ResourceAddendum < ApplicationRecord
belongs_to :resource
end
Then the abilities and this is where I am unsure of how to go about this:
...
can :manage, Resource, user_id: user.id
can :create, ResourceAddendum, resource: { user_id: user.id }
...
Is that last line correct? That feels correct but not sure how to set it up in the controller to reference the parent on a new object that's being sent via the url
The URL for the resource addendum is passing the resource id:
http://localhost:3000/resource-addendum/new?resource_id=123456
This fails and says we don't have permission because (and I'm assuming here) the Resource isn't being loaded into the new ResourceAddendum?
What's the correct way in the ability file and in the controller for resource addendums to make sure that the resource is correctly set?
Thanks
EDIT -- Adding controller action per request:
I just did this for the controller which I'm sure is entirely inadequate;
class ResourceAddendumController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource
def new
end
...
end
For the :new action, how do I preset the resource value on the resource_addendum that hasn't been created yet but should be generated by the load_and_authorize_resource method based on the resource_id param?
I have a Rails 6 app and as resource hotel.
My user model has a belongs_to hotel (hotel_id) column.
No I want to create a singular resource where a user can access its hotel directly as mentioned here:
https://guides.rubyonrails.org/routing.html#singular-resources
So I did
resource :hotel which maps to hotels#show
The idea is that I don't need to have a link hotel/ID and instead just have /hotel
But now, how does my hotels controller know which hotel to load? I assumed that it would take it from the user table but instead I get
undefined method `name' for nil:NilClass
which means that there is no #hotel in my view.
What am I doing wrong here?
You need nested routes... so in config/routes.rb you need:
resources :users do
resource :hotel
end
Then examine the route set (e.g. from the rails console do rails routes) and you'll see a set of routes that include something like this:
user_hotel GET /users/:id/hotel hotels/show
do you see the :id parameter buried in the url?
Then in the hotels controller show method, the user's id is found in the params hash. So you can find the hotel like this:
class HotelsController < ApplicationController
def show
user = User.find(user_params(:id))
#hotel = user.hotel
end
private
def user_params
params.require(:user).permit(:id)
end
end
Trying to figure our how to set up associations in form.
I have 3 models:
class Request < ActiveRecord::Base
has many :answers
has many :users, through: :answers
end
class Answer < ActiveRecord::Base
belongs to :user
belongs to :request
end
class User < ActiveRecord::Base
has many :answers
has many :requests, through: :answers
end
I am trying to figure out: how to have a User link to Answer#new from Request#Show, and then create an Answer record passing in the Request#Show request_id from the previous page - creating an association between the User's Answer and the Request he was viewing.
My method of doing this now is: I flash the request_id value on Request#Show, and then when a User links to Answer#new, it passes the flashed value into a hidden form tag on Answer#new. This does not seem like the best way to do this.
Any thoughts?
Kudos for the creative approach using flash, however your right there is an easy way. You can pass parameters much between controllers just like passing parameters between methods using the route names.
I didn't quite follow what it was you were trying to achieve in this case but it looks like this blog entry here should get you started..
https://agilewarrior.wordpress.com/2013/08/31/how-to-pass-parameters-as-part-of-the-url-in-rails/
Good luck!
User link to Answer#new from Request#Show
This can be achieved with either sessions or nested resources (or both!). Let me explain:
I would definitely add a nested resource to your requests routes:
#config/routes.rb
resources :requests do
resources :answers, only: [:new, :create] #-> url.com/requests/:request_id/answers [POST]
end
This gives you the ability to call a "nested" route (IE one which sends data to a child controller, and requires "parent" data to be appended to the request).
In your case, you want to create an answer for a request. The most efficient way is to use a routing structure as above; this will allow you to use the following controller method:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
#request = Request.find params[:request_id]
#answer = #request.answers.new
end
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.save
end
private
def answer_params
params.require(:answer).permit(:title, :body)
end
end
The above gives you the ability to create an answer by passing the request_id to the nested route. You must note the corresponding route will require a POST method in order to work.
You don't need the new method. If you wanted it, it can easily be handled with the above structure.
Passing the user is a little more tricky.
You can either use the routes, or set a session.
I would personally set a session (it's cleaner):
#app/controllers/requests_controller.rb
class RequestsController < ApplicationController
def show
session[:user_id] = #user.id #-> I don't know how you populate #user
end
end
This will give you the ability to access this session here:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
user = User.find session[:user_id]
end
end
#app/views/requests/show.html.erb
<%= link_to "New Answer", request_new_answer_path(request) %>
--
If you're using Devise, the user object should be available in the current_user object (which means you don't have to set session[:user_id]):
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
## current_user available here if using devise
end
end
To assign a #user to the new answer record, just do this in answers#create:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
...
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.user = current_user
#answer.save
end
end
Something like this worked for me:
I have two models (Formula and FormulaMaterial)
Formula has_many FormulaMaterials, which belongs to Formula
My Formula controller sets #formula like so:
#formula = Formula.find(params[:id])
I list my Formula Materials in my Formula show.html.erb by declaring it in my Formula controller like so:
#formula_materials = FormulaMaterial.where(:formula_id => #formula)
When I want to add a new FormulaMaterial to my Formula, the "New Formula Material" button in my show.html.erb file looks like this:
<%= link_to 'Add Material To Formula', new_formula_material_path(:formula_id => #formula), class: "btn btn-success" %>
In the "new_..._path" I set the associated id to the #formula variable. When it passes through to the new.html.erb for my FormulaMaterial, my URL looks like so:
http://localhost:3000/formula_materials/new?formula_id=2
In my FormulaMaterial new.html.erb file, I created a hidden_field that sets the value of the association by using "params" to access the formula_id in the URL like so:
params[:formula_id] %>
I am not sure if this is the best way to do this, but this way has allowed me to pass through the view id from the previous page as a hidden, associated and set field in the form every time.
Hope this helps!
I have a controller named 'companies' and rather than the urls for each company being denoted with an :id I'd like to have the url use their :name such as: url/company/microsoft instead of url/company/3.
In my controller I assumed I would have
def show
#company = Company.find(params[:name])
end
Since there won't be any other parameter in the url I was hoping rails would understand that :name referenced the :name column in my Company model. I assume the magic here would be in the route but am stuck at this point.
Good answer with Rails 4.0+ :
resources :companies, param: :name
optionally you can use only: or except: list to specify routes
and if you want to construct a URL, you can override ActiveRecord::Base#to_param of a related model:
class Video < ApplicationRecord
def to_param
identifier
end
# or
alias_method :to_param, :identifier
end
video = Video.find_by(identifier: "Roman-Holiday")
edit_videos_path(video) # => "/videos/Roman-Holiday"
params
The bottom line is you're looking at the wrong solution - the params hash keys are rather irrelevant, you need to be able to use the data contained inside them more effectively.
Your routes will be constructed as:
#config/routes.rb
resources :controller #-> domain.com/controller/:id
This means if you request this route: domain.com/controller/your_resource, the params[:id] hash value will be your_resource (doesn't matter if it's called params[:name] or params[:id])
--
friendly_id
The reason you have several answers recommending friendly_id is because this overrides the find method of ActiveRecord, allowing you to use a slug in your query:
#app/models/model.rb
Class Model < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
end
This allows you to do this:
#app/controllers/your_controller.rb
def show
#model = Model.find params[:id] #-> this can be the "name" of your record, or "id"
end
Honestly, I would just overwrite the to_param in the Model. This will allow company_path helpers to work correctly.
Note: I would create a separate slug column for complex name, but that's just me. This is the simple case.
class Company < ActiveRecord::Base
def to_param
name
end
end
Then change my routes param for readability.
# The param option may only be in Rails 4+,
# if so just use params[:id] in the controller
resources :companies, param: :name
Finally in my Controller I need to look it up the right way.
class CompaniesController < ApplicationController
def show
# Rails 4.0+
#company = Company.find_by(name: params[:name])
# Rails < 4.0
#company = Company.find_by_name(params[:name])
end
end
I recommend using the friendly_id for this purpose.
Please be noted that there are differences between friendly_id 4 and 5. In friendly_id 4, you can use like this
#company = Company.find(params[:id])
However, you won't be able to do that in friendly_id 5, you have to use:
#company = Company.friendly.find(params[:id])
In case that you don't want to use the params[:id] but params[:name], you have to override the route in routes.rb. For example
get '/companies/:name', to: "companies#show"
Hope these info would be helpful to you
There's actually no magic to implement this, you have to either build it yourself by correctly implementing to_param at your model (not recommended) or using one of the gems available for this like:
friendly_id
has_permalink
I use friendly_id and it does the job nicely.
Model.find(primary_key)
The default parameter here is primary_key id.
If you want to use other columns, you should use Model.find_by_xxx
so here it could be
def show
#company = Company.find_by_name(params[:name])
end
The :id parameter is whatever comes after the slash when the URL is requested, so a name attribute needs to be extracted from this by checking the :id parameter for non-numerical values with regular expressions and the match? method in the controller. If a non-numerical value is present, the instance can be assigned by the name attribute using the find_by_name() method that rails generated for the model (assuming that the model has an attribute called name)
That's how I figured out how to do it in my app with my Users resource. My users have a username attribute, and all I had to do was modify the UsersController to define my #user variable differently depending on the :id parameter:
private
# allow routing by name in addition to id
def get_user
if params[:id].match?(/\A\d+\Z/)
# if passed a number, use :id
#user = User.find(params[:id])
else
# if passed a name, use :username
#user = User.find_by_username(params[:id])
end
end
This gives me the option to use either id or username when I create a link to a particular user, or type it into the browser's address bar.
Then the only other (optional) thing to do is to change all the links in the views so that they point to the URL with the name instead of the URL with the id.
For example, within the link_to() method call in my navigation bar I changed
... user_path(current_user) ...
to
... user_path(current_user.username) ...
In your example, you might have a company view with the following link:
<%= link_to #company.name, company_path(#company.name) %>
Which, if the current company is Microsoft, would display "Microsoft" and link to "companies/Microsoft", even though the URL "companies/1" would still be valid and display the same thing as "companies/Microsoft"
In my "routes.rb" file I have the following line:
resource :users
which gives me a bunch of named routes for accessing my User model in a RESTful manner.
Now, I've made some additions to the User model including creating a special class of user. These are still stored in the User model but there is a "special" flag in the database that identifies them as special.
So, is it possible to create special_users resource? For example, I'd like to have a "special_users_path" as a named route to "/special_users" which will return an index of only the special users when you perform a GET on the URL.
Is there a way to do this?
In Rails routing, a 'resource' refers to the standard 7 routes that are created for RESTful resources: index, show, new, create, edit, update and destroy. Normally that is enough, but sometimes you might want to create another action.
In the model, you want to create a scope that only returns special users:
class User < ActiveRecord::Base
scope :special, where(:special => true)
end
On the controller side, there are two ways to go about this. What you are suggesting is the creation of an additional action:
match "/users/special" => "users#special"
resource :users
In the controller, your special action would return the scope you just created:
class UsersController < ApplicationController
def special
#users = User.special
end
end
That will do what you ask, but I would suggest NOT doing it this way. What if you add other flags later that you want to search by? What if you want to search by multiple flags? This solution isn't flexible enough for that. Instead, keep the routes the way they are:
resource :users
and just add an additional line to your controller:
class UsersController < ApplicationController
def index
#users = User.all
#users = #users.special if params[:special]
end
end
and now, when you want to display special users, simply direct the user to /users?special=true
This approach is much more future-proof, IMO.
(This answer is assuming Rails-3. If you're still using 2.3 let me know)
You could set the special_users as a resource:
resource :special_users
If you need to point it to a special controller, you could specify it with:
resource :special_users, :controller => :users
But I would really suggest you to not creating another controller for retrieving a kind of user, but using a param to get them:
class UsersController < ApplicationController
def index
users = case params[:type].to_s
when "special"
User.special_users # Using named scopes
else
User.all
end
end
end
When you use the users_path to call the special users:
users_path(:type => :special)