I have a legacy database with a composite primary key. I have successfully added the composite_primary_keys gem to the project, and can retrieve, and create new records for the object in question. The operations to update, delete or show a single record are not working, and failing with an error of
'No route matches [GET] "/estimates/4.0,Test,A,0.0'
The class is defined as:
class Estimate < ActiveRecord::Base
self.table_name = 'estimate'
self.primary_keys = [:es_gsu_id, :es_loc_name, es_blh_flag, :es_version_id]
end
And the routes.rb has been modified with:
Rails.application.routes.draw do
constraints(:id => /\w+(,\w+)*/) do
resources :estimates
end
resources :estimates
end
Which I thought for Rails 4 would have it configured correctly. I can retrieve all and create new as noted, but the single access which actually relies on specifying the PK fields is failing on the routing.
Can anyone see what I'm doing wrong or what I've missed?
I was facing the exact same problem and after lots of tests found the following solution:
First of all, since you are deviating from default 'id' primary key assigned by Rails, you need to add this to your routes.rb:
resources :estimates, param: :es_gsu_id
Just mentioning one of the attributes of your composite key in 'param' hash will do.
Secondly, you don't need this piece of code
Rails.application.routes.draw do
constraints(:id => /\w+(,\w+)*/) do
resources :estimates
end
Now, your GET request to /estimates/4.0,Test,A,0.0 will be routed to 'show' action of 'estimates' controller where you can retrieve the resource using 'find' method like this:
resource ||= Estimate.find(params[:es_gsu_id])
params[:es_gsu_id] will have the value '4.0,Test,A,0.0' which will be used by the 'find' method to search for a unique record matching this composite key.
Related
TL;DR: I want to have username621/posts/title-of-post instead of member/posts/1
The changing of post id to post title was easy enough since I used the freindly_id gem to generate the slugs.
However, I am having difficulty routing to a personalized params route instead of the current namespaced route. Here is the current routing:
namespace :member do
resources :posts
end
I want to replace the member namespace to user's username. So if their username is user123, the route should be user123/posts/title-of-post.
I think that this is not very standard Rails routing and tried looking for similar questions with no results.
for more complicated routes.rb, add a path option
namespace :member, path: ":user_id" do
resources :posts
end
should get what you want, e.g. http://localhost:3000/621/posts/1
then we just have to add friendly_id to User and Post to have it become something like http://localhost:3000/username621/posts/title-of-post
however, you'll need to pore through the codebase for things like member_post_path(post) and change to member_post_path(post.user, post)
Try removing the namespace and adding path option:
resources :posts, path: '/:username/posts/'
Then if you access /username621/posts/title-of-post in your controller you'll see params[:username] = 'username621'
If you have other paths of the form /something/posts add them above this route, otherwise they will be caught by :username.
In my app I have a User model which defines a history method that returns a list of Activity objects, showing the last N actions the user has carried out. The UserController#history method wires this with a view.
The code looks as follows:
class UserController < ApplicationController
def history
user = User.find(params[:id])
#history = user.history(20)
end
end
class User < ActiveRecord::Base
has_many :activities
def history(limit)
...
end
end
Naturally, I also added this line to my routes.rb file:
match '/user/:id/:action', :controller => 'user'
so now when I go to localhost:3000/user/8/history I see the history of user 8. Everything works fine.
Being a Rails NOOB I was wondering whether there is some canned solution for this situation which can simplify the code. I mean, if /user/8 is the RESTful way for accessing the page of User 8, is it possible to tell Rails that /user/8/history should show the data returned by invoking history() on User 8?
First of all the convention to name controllers is in the plural form unless it is only for a single resource, for example a session.
About the routes I believe you used the resources "helper" in your routes, what you can do is specify that the resource routes to users also has a member action to get the history like this
resources :users do
member do
get :history
end
end
I think there is no cleaner way to do this
You can check it here http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
As far as the rails standards are concerned, it is the correct way to show the history in your case. In rails controllers are suppose to be middle-ware of views and model, so defining an action history seems good to me.
And you can specify the routes in better way as:
resources :user do
get 'history', :on => :member #it will generate users/:id/history as url.
end
I have a question about browser string in rails.
For example i have rails app with routes:
resources :posts
and this resource create :
post/:id
post/21
post/167
post/356
but i create a simple blog and i want to rename ':id' to
post/some-name
post/another-name
post/another-different-name
in post i have title, text field
but i dont know how do this
I know that this can be achieved through manipulation of the :id
can you post some link with detailed answer on this question, or some simple example
You can of course put anything you want in the URL and actually there is railcast about it:
http://railscasts.com/episodes/63-model-name-in-url
It is preferable (read: easier) to also keep model.id in the URL, or it means that post name MUSTÂ be unique, otherwise you can put anything you want:
/post/2465-my-pretty-post-name
Also, there is a gem friendly_id and related railcast:
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
Hope that helps.
Why do you want to change /post/:id ?
You can achieve something like /post/:id/comments
You can do that using nested resources like this in your routes.rb
resources :posts do
resources :comments
end
Check here for more details
http://guides.rubyonrails.org/getting_started.html
If you add the to_param method to the model then you can use that within your URL system.
class SomeModel < ...
def to_param
self.title
end
end
Then inside your controller, setup a filter to fetch the model using the title attribute instead of the ID attribute which is used for the find method.
before_filter :setup_record
def setup_record
#record ||= Record.find_by_title(params[:id])
end
You will have to ensure that your title stays unique and if you change it then you will either have to discard all other previous URLS or keep a history of older names.
When I click the 'New Schedule Status' button on the 'Project' show page, but the route that the error shows me is plural, when it should be singular. Here's my code:
# project.rb
class Project < ActiveRecord::Base
has_one :schedule_status
end
# schedule_status.rb
class ScheduleStatus < ActiveRecord::Base
belongs_to :project
end
# schedule_statuses_controller.rb
def new
#project = Project.find(params[:project_id])
#schedule_status = #project.build_schedule_status
end
# routes.rb
resources :projects do
resource :schedule_status
end
# _form.html.erb
<%= form_for [#project, #schedule_status] do |f| %>
...
The error informs me that my form_for line is incorrect. It seems like my instance variables are setup correctly, but the error is:
undefined method `project_schedule_statuses_path` for ...
Any idea why the route it's attempting to access is plural?
This is a bug. form_for looks for the plural version of the object. However since you've declared a singular resource :schedule_status, the path helper method is never created.
To get around this you should use :url parameter for form_for.
Look at this question/answer for more clarity.
It is not a bug it is a feature (ticket closed as won't fix):
rails issue:
https://github.com/rails/rails/issues/1769
summary quote:
the error has been around for a long while however a clean solution
doesn't readily represent itself. The polymorphic_url helper has no
'intelligence' in how it operates - it has no information about what
you've declared as resources in your routes.rb. All it has to go on it
the name of the model and how that maps to the named url helpers.
The problem is there is no easy way to discern whether a model maps
to a singular or a regular resource url. Checking for the presence of
a collection url doesn't work as the resource may have been specified
with :except => :index and trying to rescue route generation errors
doesn't work because passing an instance to a singular resource url
helper will generate a url with the format set to the id and no
exception.
rails issue closed in favour of the previous:
https://github.com/rails/rails/issues/4978
conclusion:
in such cases you're supposed to give the url. url_for cannot reflect
on routes to see if that's a resource or not.
Working in Rails 3.2, I a polymorphic Subscription model whose subscribable_type may or may not be a nested resource. I'm trying to display the full URL link in an email view, but have no knowledge whether or not that resource is nested.
When I try url_for #model on a nested resource, it fails, expecting url_for [#parent, #model]. Unfortunately, I do not know how to discover the parent as defined in the Routes table.
Is there a way to identify the route path for a nested resource? If I could match the model to a route, I could fill in the necessary IDs.
As of right now, I've defined a method in my models called parent_resource :model that can be traversed, but I'm hoping there's a better way.
Within my routes.draw:
resources :projects do
resources :topics do
resources :comments
end
end
resources :subscriptions
(I realize I shouldn't be nesting so deeply)
Edit: Additional Information
My Subscription model is a resource I use to manage notifications. Subscribable types are provided a link that toggles the subscription for that user on that subscribable_type / subscribable_id on or off.
I then go through a Notifier < ActionMailer::Base which is provided the Subscription instance, and mail the user.
Through that setup, I'm trying to get the full url of subscription.subscribable which may be a Topic or a Project.
I realize that I could hammer out the conditions in this small case through a helper method, but I am curious to know how one would approach this if there were dozens of nested model pairs.
You mention subscription but your routes are completely different. I'm guessing the routes you gave were just an example then. I would start with trying to get rid of the custom parent_resource method you created. You can probably do the same thing simpler with adding a belongs_to through and maybe with conditions if you need too:
belongs_to :projects, :through => :topics, :conditions => ['whatever your conditions are']
I'd have one of those per parent type so I can do things like:
object.project.present?
And from there I could easily know if its nested or not and simplify things by letting rails do the parent traversal. That ought to simplify things enough to where you can at least figure out what type of subscription you have pretty easily. Next, I'd probably add some matched routes or try to cram an :as => 'somename' into my routes so I can call them directly after determining the nested part. One option would be something like this:
match "projects/subscription/:id" => "projects#subscription", :as => :project_subscription
match "other/subscription/:id" => "other#subscription", :as => :other_subscription
And so its pretty obvious to see how you can just specify which url you want now with something like:
if #object.project.present?
project_subscription_path(#object)
else
other_subscription_path(#object)
end
This may not be the best way to accomplish what I'm doing, but this works for me right now.
This builds a nested resource array off the shortest valid route helper and generates a URL:
(Tested in rails console)
resource = Comment.first
resource_name = resource.class.to_s.downcase
helper = Rails.application.routes.named_routes.helpers.grep(/.*#{resource_name}_path$/).first.to_s.split('_')
built = helper.slice!(-2,2) # Shortest possible valid helper, "comment_path"
while !(app.respond_to?(built.join("_").to_sym))
built.unshift helper.pop
end
built.pop # Get rid of "path"
resources = built.reverse.reduce([]) { |memo, name|
if name == resource_name
memo << resource
else
memo << memo.last.send(name.to_sym) # comment.topic, or topic.project (depends on belongs_to)
end
}
resources.reverse!
app.polymorphic_url(resources) # "http://www.example.com/projects/1/topics/1/comments/1"