I start to learn ruby on rails and I want to temporary add an attribute to a model so I added the accessor to my model :
attr_accessor :dettes
And then I used the method each, here is what I have :
#depenses = Depense.where(user_id: #user.id)
#depenses.each do |d|
d.dettes = "value"
end
render json: #depenses
But this didn't work, my new attribute isn't added
I've seen others questions about this but I'm still not able to resolve this, am I missing something ?
By default, render :json only adds the permanent attributes. You can add the temporary one as well this way:
render json: #depenses, methods: :dettes
As far as I understand you want modify object within request without mutating it in the database, so you add attr_accessor.
Then you used
#depenses = Depense.where(user_id: #user.id)
Which returns instance of ActiveRecord::Relation(not explicit instances of objects which you can modify). So you when you iterates within this relation instance and change it attr_accessor the instance of modyfied object hasn't been assigned to any public variable. Basically you need to convert relation to array, then #depenses going to be an array of Depense instance objects, which you can modify and pass to render methods:
#depenses = Depense.where(user_id: #user.id).to_a
Then you need tell json that you want to use custom method for serializing, like #eiko mentioned:
render json: #depenses, methods: :dettes
(But if you have a loot of data in Depense datatable then it's not the best idea, you gonna use some limitations with offsets for where clause)
Keep rocking ;)
Related
Is is possible to create a custom model attribute and get its value included by default, whenever that model is called?
I have a model named Video. It has an attribute named name. It contains the name of the video sample-video.mp4. I want to create two custom attributes named iphone_url and android_url for model.
Both attributes will have different urls concatenated with name. So, iphone_url will have http://link1/+name+/playlist.m3u8 where as android_url will have http://link2/+name
Is it possible that whenevr i call that model, both attributes are automatically added(in JSON response)?
I tried solutions mentioned here.
I was able to add custom attributes by using attr_accessor, but their value is always null. May be because their value needs to be set manually first.
So how to do this?
Edit: Currently, i'm doing like this:
videos = Array.new
# Dirty, but works
Video.all.each do |video|
video = video.attributes
wowza_server = "X.X.X.X:XXXX/AppName/"
custom_attributes = {:wowza_urls => {:ios => "http://"+wowza_server+"mp4:"+video[:name])+"/playlist.m3u8", :android => "rtsp://"+wowza_server+video[:name])}}
videos << video.merge(custom_attributes)
end
render :json => videos, status: :ok
This is really what jbuilder was made for.
Jbuilder gives you a simple DSL for declaring JSON structures that beats massaging giant hash structures.
You're starting to massage a giant hash structure.
I'm assuming this is an index view, in which case it look something like this:
json.array! #videos do |video|
json.my_field video.my_field
json.iphone_url video.iphone_url
json.android_url video.android_url
end
On your app/model/video.rb
class Video
def iphone_url
"some_url_to_build"
end
...
end
Is there any way to remove sensitive fields from the result set produced by the default ActiveRecord 'all', 'where', 'find', etc?
In a small project that I'm using to learn ruby I've a reference to User in every object, but for security reasons I don't want to expose the user's id. When I'm using a simple HTML response it is easy to remove the user_id simply by not using it. But for some task I'd like to return a json using something like:
def index
#my_objects = MyObject.all
respond_to do |format|
...
format.json { render json: #my_objects, ...}
...
end
end
How do I prevent user_id to be listed? Is there any way to create a helper that removes sensitive fields?
You can use the as_json to restrict the attributes serialized in the JSON response.
format.json { render json: #my_objects.as_json(only: [:id, :name]), ...}
If you want to make it the default, then simply override the method in the model itself
class MyObject
def serializable_hash(options = nil)
super((options || {}).merge(only: [:id, :name]))
end
end
Despite this approach is quick and effective, it rapidly becomes unmaintainable as soon as your app will become large enough to have several models and possibly different serialization for the same type of object.
That's why the best approach is to delegate the serialization to a serializer object. It's quite easy, but it will require some extra work to create the class.
The serializer is simply an object that returns an instance of a model, and returns a JSON-ready hash. There are several available libraries, or you can build your own.
I'm using RABL right now to generate JSON responses of an API in Rails, but I'm finding that while RABL is super handy for mapping models to responses, to create a consistent API I'm having to to duplicate that mapping logic in the update and create functions of my controller.
As a simple example, if I just want to change the attribute names in the response to a POST request, I can do this in RABL:
create.rabl
object #car
attributes car_id: :id, badly_named_legacy_column_that_means_color: :color
But if I want the client to be able to use these same "cleaned up" attributes in the JSON POST/PUT request itself (i.e. be able to send { "id": 1, "color": "red" } instead of { "car_id": 1, "badly_named_legacy_column_that_means_color": "red" }), I have to manually do this mapping again in the controller:
cars_controller.rb
def create
params[:car_id] = params.delete(:id)
params[:badly_named_legacy_column_that_means_color] = params.delete(:color)
#car = Car.create(params)
end
Now there are two places that I need to map car_id to badly_named_legacy_column_that_means_color. Not very DRY.
So far I haven't come across any way to handle this using RABL. Is there one that I'm missing? I also realize this might be outside the scope of RABL, which bills itself specifically as a templating system, so maybe is there another API builder that would allow me to do this? I love the idea of mapping messy database columns to a clean API but having to specify this mapping in both the view and the controller isn't very DRY. Any thoughts appreciated.
Update
The original answer is all about Ruby/Rails => JSON, the question is JSON => Ruby/Rails. This answer about associating columns should explain an approach:
alias_attribute :new_column_name, :column_name_in_db
Then you can just reference new_column_name in the RABL and Rails will handle the association on the create/update.
You should be able to call render from the create method and render any view. You could customize a response with a create specific template or reuse the generic show template. The trick is to re-use the object rabl template (app/views/car/car.rabl in this case), for example:
# POST /cars
def create
#car = Car.new(params)
if #car.save
render action: 'show'
else
respond_with #car
end
end
Where app/views/cars/car.rabl is
attributes :id, ...
and app/views/cars/show.rabl is
object #car
extends "cars/car"
I am creating a instance variable that gets passed to my view. This variable 'post' has a user_id associated with it and I wanted to add an extra attribute called 'username' so I can also pass that and use it in the view.
Here is an example of what I would like to do.
#post = Post.find(params[:id])
#post.username = User.find(#post.user_id).username
A username column does exist on my Users model but not my Songs model. So it won't let me use
#post.username
I know I can just make an entirely new instance variable and put that information in there but I would like to keep everything nice and neat, in one variable. Which will also make my json rendered code look cleaner.
Any ideas on how I can accomplish this?
Thanks!
Based on the presence of a user_id in your Post model, you probably already have an association set up that can retrieve the username. It will probably save a lot of trouble to simply use the existing association:
#post = Post.find(params[:id])
username = #post.user.username
If you're likely to be querying more than one post at a time (e.g., on an index page, calling .includes to tell Rails to eager-load an association will help you avoid the N+1 problem:
#posts = Post.includes(:user).all
Finally, to include the associated record in your JSON output, pass the :include parameter as you serialize:
# in controller
render :json => #post.to_json(:include => :user)
This question includes a much more comprehensive discussion of serialization options. Well worth a read.
No need to pass a separate instance variable.
1. You can use #post.user.username in view itself.
2. Or you can create a helper and pass #post.user
def username user
user.username
end
So I have a model called Image that belongs_to :user. Each user has a first and last name.
I have a flash app that I am returning a json object back to of Images.
the service I will be calling on the Images controller would look something like this
def getimages
#images = Image.all
render :json => #images
end
My json would look something like this
[{"image":{"created_at":"2011-01-22T19:04:30Z","img_path":"assets/img/bowl_93847566_3_0.png","updated_at":"2011-01-22T19:04:30Z","id":9,"user_id":3}}]
what I would like to do is also include the users first and last name with in the image object that gets passed back.
once I have an image object I am able to do something like image.user.first_name but I am not clear how I would return something like an array of image objects and include the user along with it.
what would be great is if I could get my array of images to look like the following.
[{"image":{"created_at":"2011-01-22T19:04:30Z","img_path":"assets/img/bowl_93847566_3_0.png","updated_at":"2011-01-22T19:04:30Z","id":9,"user_id":3, "first_name":"Matthew", "last_name":"Wallace"}}]
I am thinking this may include adding some kind of model method or somthing that I am not familiar with.
What would be the best practice for achieving this?
You could:
render :json => #images.to_json(:include => :users)
See http://apidock.com/rails/ActiveRecord/Serialization/to_json (and http://apidock.com/rails/Array/to_json shows it works on Arrays). Finally, http://apidock.com/rails/ActionController/Base/render describes using to_json in a json render as optional and not required, which implies it should cause no harm (I couldn't see another way to pass the required options in).
Perhaps cleaner json:
render :json => #images.to_json(:include => { :user => { :only => [:first_name, :last_name] } })
Besides the answer provided by #apneadiving, you can also override the Image's to_json method and return a string containing whatever JSON you need.