Rails - find_by column other than "id" results in "null" - ruby-on-rails

Been trying to get this working for the past several hours, I'm probably over looking something quite simple. Basically I have a "posts" model, and one of the columns I have created is named guid, which is just a user definable field that gets saved to the database.
I'm looking to find_by the guid instead of the default id.
Of course I've tried #post = Post.find_by_guid(params[:guid]) in my posts controller show action, but when I attempt to call it from the url (ex: mysite/post/53), in the server console it says guid is NULL
Webrick Console:
Processing by PostsController#show as HTML
Parameters: {"id"=>"53"}
Post Load (0.4ms) SELECT "posts".* FROM "posts" WHERE "posts"."guid" IS NULL LIMIT 1
....

if the route is
get "/posts/:id"
Then you will its the id that is going to be placed in the params which you can see in the output. So use params[:id]
#post = Post.find(params[:id])
You should change your route if you are passing a GUID:
get "posts/:guid" ...
Hit the url "posts/21EC2020-3AEA-1069-A2DD-08002B30309D"
And then in the controller:
#post = Post.find_by guid: params[:guid]
Rails passes instance variables (any variable starting with #) set in the controller to the views.
Referencing instance variables in the view that the controller did not set will return nil, which does not have any of the methods you are going to call on it, hence the "No method" errors.
Any time you run into trouble, check your assumptions at the console. I recommend using Pry. Using Pry (calling "binding.pry" in your code) you can actually stop the execution at any point and look around. That way you never have to guess.

The find_by_[column] method is deprecated.
Use the following syntax: find_by guid: params[:guid]

Your parameters hash does not contain a :guid key. If you were to use mysite/post/53?guid=SOMETHING then you will see a :guid key in params.
Do you want the 53 in this example to be the GUID parameter? There are ways to change the Rails routes to get that parameter to be guid instead of id, but if 53 is the GUID, you can always go:
Post.find(:guid => params[:id]).first

Related

How can I selectively add query parameters in redirect_to?

In my application, the session hash can contain the keys sort and ratings (in addition to _csrf_token and session_id), depending on what action a user takes. That is, it can contain both of them, either one of them, or neither, depending on what a user does.
Now, I wish to call redirect_to in my application and, at the same time, restore any session information (sort or ratings) the user may have provided.
To do this, I want to insert whatever key-value session has currently got stored (out of sort and ratings) as query parameters in my call to redirect_to. So, the path might look something like /movies?sort=...&ratings=....
I don't know how to write the logic for this. How can I do this? And how do I go about selectively inserting query parameters while calling redirect_to? Is it even possible to do this?
Any help is greatly appreciated. Thank you in advance.
First just compose a hash containing the parameters you want - for example:
opts = session.slice(:sort, :ratings)
.merge(params.slice(:sort, :ratings))
.compact_blank
This example would contain the keys :sort, :ratings with the same keys from the parameters merged on top (taking priority).
You can then pass the hash to the desired path helper:
redirect_to foos_path(**opts)
You can either just pass a trailing hash option or use the params option to explitly set the query string:
irb(main):007:0> app.root_path(**{ sort: 'backwards' })
=> "/?sort=backwards"
irb(main):008:0> app.root_path(params: { ratings: 'XX' })
=> "/?ratings=XX"
irb(main):009:0> app.root_path(params: { })
=> "/"
An empty hash will be ignored.
If your calling redirect_to with a hash instead of a string you can add query string parameters with the params: key:
redirect_to { action: :foo, params: opts }
If you're working with an arbitrary given URL/path and want to manipulate the query string parameters you can use the URI module together with the utilities provided by Rack and ActiveSupport for converting query strings to hashes and vice versa:
uri = URI.parse('/foo?bar=1&baz=2&boo=3')
parsed_query = Rack::Utils.parse_nested_query(uri.query)
uri.query = parsed_query.except("baz").merge(x: 5).to_query
puts uri.to_s # => "/foo?bar=1&boo=3&x=5"

Rails query by arbitrary column

In my Rails API / Angular app, I want to be able to search Rails tables using field values. I currently have this code below working, which allows searching the users table by email id, and it returns the users record as JSON.
api/controllers/users_controller.rb
def query # by email
queried_user = User.where(email: params[:email]).first
if !queried_user.nil?
render json: queried_user, root: false
else
render json: {error: 'Does not exist'}, status: :not_found
end
end
config/routes.rb
get 'api/users/:id/query' => 'api/users#query'
Example url
http://0.0.0.0:8080/api/users/1/query?email=testuser1#example.com
Example returned JSON
{"id":14,"title":"Dr.","first_name":"John","last_name":"Smith","email":"testuser1#example.com","job_title":"Head Bioligist","organisation":"NIH","phone_office":null,"city":null,"country":null,"approved":true,"admin":false,"template":false}
This is all working fine at present, but there are two issues I cannot resolve.
I would like the url to not contain an :id I find when I leave the id out of the url, Rails treats the query parameter as the id. I can made it work by hard-coding a fake id, but it doesn't seem like the right answer to me.
I would like to pass an abitary param hash to the query method. It should map the columns based on the hash contents.
if params = {email: 'testuser1#example.com'} then it should work as now, but other desired options might be:
{job_title: 'Manager'}
{city: 'LA', last_name: 'Smith'}
I expect I will change this code, but don't know how to pass arbitrary elements to the where.
queried_user = User.where(email: params[:email])
The where method can accept a hash, therefore you can pass the param hash containing the condition for the query. Just note only equality and range conditions can be used when passing a hash to the where method. Just be sure that in terms of security of your application you are covered. example:
queried_user = User.where(params[:user])
To get rid of the :id in your routes file define a new route similar to this:
match 'api/users/query', to: 'users#query', as 'user_search'
and then use the 'user_search_path' for sending the search to the query action of the users controller.

undefined method `[]' for nil:NilClass in controller

I have parameter passing in console shows as:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Oj9EGihSOwgdXGLLQWqVESYMP/N4K0KzDS4KyVhWXPg=", "rfp"=>{"user_id"=>"", "client_id"=>"", "othercms"=>"", "otherecommerce"=>"", "numberofpage"=>"", "designcomplexity"=>"", "browser"=>"", "nuhacks"=>"", "nujavascript"=>"", "numberofmenu"=>"", "designpages"=>"", "designformobilepages"=>"", "framworks"=>"", "test_report_ids"=>[""], " payment_gateway_ids"=>[""], "payment_gateway_ids"=>["2"], "**payment_gateways"=>{"name"=>"slsk"}**, "commit"=>"Create Rfp", "project_id"=>"18"}
Controller:
#rfp = Rfp.new(params[:rfp])
if [:payment_gateway][:name]
#pm=PaymentGateway.new([:payment_gateways][:name])
end
as payment gateway is independent model:
Even though paymengt gateway name passing in params it shows above error. What is missing? Please give me any help. Thanks in advance.
I think you dont understand data types.
On first line, you initialized new instance of Rfp class, and then you are trying to retrieve index of nothing, instead of array or hash.
There are two solutions for this.
I noticed that payment_gateways are inside rfp parameters, so i guess its association or attribute of it, so you can check "show me all the names of payment_gateways in newly initialized object"
if #rfp.payment_gateways.map(&:name).any?
Check in params:
if params[:rfp].present? and params[:rfp][:payment_gateways].present? and params[:rfp][:payment_gateways][:name].present?
After that, initialize your PaymentGateway instance:
`#pm = PaymentGateway.new(params[:rfp][:payment_gateways])`
As per your code it should be if params[:rfp][:payment_gateways][:name] not if [:payment_gateway][:name]
So It should look like
as your incomplete params there is rfp as well so it might be params[:rfp][:payment_gateways][:name]
if params[:rfp][:payment_gateways][:name]
#pm=PaymentGateway.new(params[:rfp][:payment_gateways][:name])
end
or inliner
#pm=PaymentGateway.new(params[:rfp][:payment_gateways][:name]) if params[:rfp][:payment_gateways][:name]
In Controller, it should be:
#rfp = Rfp.new(params[:rfp])
if params[:payment_gateway]
#pm=PaymentGateway.new(params[:payment_gateways][:name])
end
or even better
#rfp = Rfp.new(params[:rfp])
#pm=PaymentGateway.new(params[:payment_gateways][:name]) unless params[:payment_gateway].nil?
also check, whether it should be params[:payment_gateway] or params["payment_gateway"].

Returning a virtual attribute in a select query

Part of an application I'm building is an API. Recent changes mean that I need to put two different versions of the data into my json feed. I think the best way to do this is to make the necessary changes in the database then create a virtual attribute to concatenate the data.
In my model I have the event_summary virtual attribute which there's no issue with outputting in views using <%= #event.event_summary =>:
def event_summary
"#{title} (#{start_datetime.strftime('%A %d %b, %l:%M%P')})"
end
In my API controller I have a select query which gets the attributes I need for the API response (simplified version here):
respond_to :json
def index
respond_with Event.select('title, event_summary')
end
The problem is that it always returns an undefined column error:
PG::UndefinedColumn: ERROR: column "event_summary" does not exist LINE 1: SELECT title, event_summary FROM "events"
I also tried the following but got the same result:
respond_with Event.select('title', :event_summary)
I'm using Postgres if that makes any difference.
Is there a way to do this?
Thanks!
You can't use virtual attributes in the select method because it will be turned into a SQL statement and that field doesn't exist in your table. You could however, do the concatenation and date formatting in SQL:
Event.select('title, (title || ' ' || to_char(start_datetime, 'HH12:MI:SS')) as event_summary')
That will in effect create a "virtual attribute" but in sql land and it will return events with that attribute present and formatted by the to_char postgres method. The date formatting isn't exactly what you had, so you'll need to tweak it to your needs (by changing the format string 'HH12:MI:SS') as detailed in the docs here: http://www.postgresql.org/docs/8.2/static/functions-formatting.html
Hope that helps.
I think this might do what you want:
def index
render :json => Event.all.to_json :methods => :event_summary
end
[EDIT]
IIRC respond_with doesn't work with to_json, so you have to use render

Private Method for Nested Resource

From Previous:
Rails 4 Nested Resources/Routes... almost there...?
My private method within my lines controller to load the manufacturer into the controller is throwing an error...
I use before_filter :load_manufacturer
and the function is:
def load_manufacturer
#manufacturer = Manufacturer.find(params[:manufacturer_id])
end
When I try to edit the line instance in the form, I get:
Couldn't find Manufacturer with id=manufacturer_id
But it is passing the manufcaturer params ok...
Parameters:
{"manufacturer_id"=>"manufacturer_id",
"id"=>"17"}
Your manufacturer id is incorrectly set. Its set to string "manufacturer_id" instead of an integer id value(in String format). The problem lies else where. As you can see
{"manufacturer_id"=>"manufacturer_id"
Should look something like
{"manufacturer_id"=>"1"
manufacturer_id should be an integer value

Resources