accessing multiple values from hash ruby on rails - ruby-on-rails

This code snippet
room = Room.find(roomId)
returns a single column from room table, the returned value contains multiple attributes, like id, name, description, duration.
I want when coding
render json: room
to return only duration and name.
Do i have to write something like that
render json: room[:duration, :name]

query that will only give you the attributes that you want :
room = Room.select(:id, :duration, :name).find(room_id)

You can use the only option of as_json to include only certain attributes.
render json: room.as_json(:only => [:duration, :name])

You're slightly incorrect when you say Room.find(roomId) returns a "single column from the room table". It actually returns a single row.
Anyway, there are a few ways you can do this.
A good starting point is to use the .attributes method on a model to convert it to a Hash.
So for example say you have a user with a name, email, and id columns in the database. You can call user.attributes to get
{ "name" => "max", "email" => "max#max.com", "id" => 5 }.
you can select just the name and email with
user.select { |k,v| k.in?("name", "email") } and then call to_json on the result.
To avoid having to write custom to_json code for each controller response, you can overwrite .attributes in the model. Say my User table has a password column that I never want to expose in my API:
class User
def attributes
super.reject { |k,v| v.in?("password") }
end
end
Then, if you need to return a list of users as JSON:
render json: { users: #users.map(&:attributes) }.to_json, status: 200
The answer by Pavan will work as well, by the way.
Another approach is to use jbuilder and create json "views" for your records.

Related

Convert field from integer to string in one rails Active Record relation before sending json

In my Rails 4 app, I actually send an active record relation in JSON with:
[...]
wine['varietals'] = record.varietals
#wines << wine
format.json { render :json => { :success => "OK", :items => #wines } }
[...]
wine['varietals'] is an array of AR relations. My problem is the varietal model contains a field named grape_id that is an integer. I need to send it in string for my WS. I don't want to make a custom conversion to JSON just for this field.
How to force this field to be string before the automatic JSON conversion ? If possible I don't want to make an array of hashes and keep the AR style with dot: model.field
wine['varietals'].each do |varietal|
varietal.grape_id.to_s
end
Of course this doesn't work.
All Rails models have an as_json method that gets called when rednering the model to JSON. You can override this method within your models to set up custom JSON formatting. In your case, you may want to add something like this to your Wine model:
def as_json(opts = {})
json = super(opts)
json["grape_id"] = self.grape_id.to_s
json
end
The method gives you the default model JSON when you call the super method and set it to the json variable, then stringifies grape_id and sets it in the JSON, and finally returns the updated JSON.
Now, any time a controller returns a JSON version of single Wine model, or an association of multiple Wine models, the JSON will be formatted through this updated method and the grape_id will be stringified every time.

Recive JS object and fetch data from database

I am using Angular in frontend and I am trying to send object such as:
obj = {
"foo" : "1",
"bar" : 2,
"baz" : 3
}
And when Rails recives this object, it should send back data from database depending on these parameters. This is my current method to create API:
def index
#tickets = Ticket.select(
'id',
'departure_date',
'departure_country',
'departure_country_tag'
)
render status:200, json: { tickets: #tickets}
end
However this fetches all data from database. I only want to fetch data that matches parameters from the object.
Any suggestion on how to do this properly?
Select allows you to specify the attributes to be returned. You need to add a clause to that query with the values from the front end.
#tickets = Ticket.select(
'id',
'departure_date',
'departure_country',
'departure_country_tag'
).where(foo: obj["foo"], bar: obj["bar"], baz: obj["baz"])
If you look in the console you'll see how the data is being passed and you can amend the active record query accordingly.

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.

Mapping through a hash of key/values

I have a form being submitted that is saving multiple records, and the parameters look something like this:
{
"utf8"=>"✓",
"_method"=>"put",
"products"=> {
"321" => {
"sale_price"=>"10"
},
"104" => {
"sale_price"=>"10"
}
}
}
Then in my controller, I have this:
#updated_products = Product.update(params[:products].keys, params[:products].values)
This expects the keys (321, 104) to be IDs.
However, I'm using the to_param in my model to change my urls from IDs to another column value.
Is there a way to take the params[:products].keys and swap them for the appropriate IDs so I can use IDs in the .update() statement.
I can use Product.find_by_column_name(321).id to get the id although I don't know how to do this. Still new to rails.
Any help would be appreciated. Thanks.
Looking at the source code here #update iterates through each key and runs update_attributes so it goes through all the validations. You can change your method to
#updated_products = params[:products].inject([]) do |array, (column_id, attributes)|
product = Product.find_by_column_id column_id
if product.update_attributes(attributes)
array << product
else
array
end
end
This may seem a little complex but it is equal to this one below which is easier to understand and code read
#updated_products = []
params[:products].each do |column_id, attributes|
product = Product.find_by_column_id column_id
if product.update_attributes(attributes)
#updated_products << product
end
end

Rails: How do I unserialize from database?

I am currently trying to save information for an invoice/bill. On the invoice I want to show what the total price is made up of. The procedures & items, their price and the qty. So in the end I hope to get it to look like this:
Consult [date] [total_price]
Procedure_name [price] [qty]
Procedure_name [price] [qty]
Consult [date] [total_price]
Procedure_name [price] [qty]
etc...
All this information is available through the database but i want to save the information as a separate copy. That way if the user changes the price of some procedures the invoice information is still correct. I thought i'd do this by serializing and save the data to a column (consult_data) in the Invoice table.
My Model:
class Invoice < ActiveRecord::Base
...stuff...
serialize :consult_data
...
end
This is what I get from the form (1 consult and 3 procedures):
{"commit"=>"Save draft", "authenticity_token"=>"MZ1OiOCtj/BOu73eVVkolZBWoN8Fy1skHqKgih7Sbzw=", "id"=>"113", "consults"=>[{"consult_date"=>"2010-02-20", "consult_problem"=>"ABC", "procedures"=>[{"name"=>"asdasdasd", "price"=>"2.0", "qty"=>"1"}, {"name"=>"AAAnd another one", "price"=>"45.0", "qty"=>"4"}, {"name"=>"asdasdasd", "price"=>"2.0", "qty"=>"1"}], "consult_id"=>"1"}]}
My save action:
def add_to_invoice
#invoice = #current_practice.invoices.find_by_id(params[:id])
#invoice.consult_data=params[:consults]
if #invoice.save
render :text => "I think it worked"
else
render :text => "I don't think it worked'"
end
end
It does save to the database and if I look at the entry in the console I can see that it is all there:
consult_data: "--- \n- !map:HashWithIndifferentAccess \n consult_da..."
(---The question---)
But I can't seam to get back my data. I tried defining a variable to the consult_data attribute and then doing "variable.consult_problem" or "variable[:consult_problem]" (also tried looping) but it only throws no-method-errors back at me.
How do I unserialize the data from the database and turn it back into hash that i can use?
Thank you very much for any help!
I think you should determine class of object
IE:
serialize :consult_data, Hash
So then
invoice = Invoice.last
invoice.consult_data[:date] # will return date
etc

Resources