Rails render object in json with model associations - ruby-on-rails

I'm trying to render the user model along with the posts model, but I'm having trouble figuring out what the syntax for it would be
class Post < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
has_many :posts, dependent: :destroy
end
Controller
def map_locations
#posts = Post.where.not(location: [nil, ""])
render :json => #posts.as_json(only: [:topic, :location, :latitude, :longitude],)
end
Output:
[{"topic":"Garret ATX","location":"Hornitos, CA, USA","latitude":37.5021592,"longitude":-120.238241}]
Desired Output:
[{"user_name":"Randy","topic":"Garret ATX","location":"Hornitos, CA, USA","latitude":37.5021592,"longitude":-120.238241}
The user model has #user.user_name which is the one I need for each post.
How do I render the user associated with each post?

I hope this works
def map_locations
#posts = Post.where.not(location: [nil, ""])
render :json => #posts.as_json(:only => [:topic, :location, :latitude, :longitude], :include => {:user => {:only => :user_name}})
end
Please check the apidock for more details.

Related

rails to_param is not working

I am following Ryan Bates railscasts video of friendly url. I am trying to implement that on my Category model by overriding the to_parammethod.
Seems like it's not working, or I am missing something.
Below is my url before overriding:
localhost:3000/search?category_id=1
After overriding the to_param the url remained same.
Following is my code:
Category model
class Category < ActiveRecord::Base
enum status: { inactive: 0, active: 1}
acts_as_nested_set
has_many :equipments, dependent: :destroy
has_many :subs_equipments, :foreign_key => "sub_category_id", :class_name => "Equipment"
has_many :wanted_equipments, dependent: :destroy
has_many :services, dependent: :destroy
validates :name, presence: true
validates_uniqueness_of :name,message: "Category with this name already exists", scope: :parent_id
scope :active, -> { where(status: 1) }
def sub_categories
Category.where(:parent_id=>self.id)
end
def to_param
"#{id} #{name}".parameterize
end
end
Controller
def search_equipments
begin
if (params.keys & ['category_id', 'sub_category', 'manufacturer', 'country', 'state', 'keyword']).present?
if params[:category_id].present?
#category = Category.active.find params[:category_id]
else
#category = Category.active.find params[:sub_category] if params[:sub_category].present?
end
#root_categories = Category.active.roots
#sub_categories = #category.children.active if params[:category_id].present?
#sub_categories ||= {}
Equipment.active.filter(params.slice(:manufacturer, :country, :state, :category_id, :sub_category, :keyword)).order("#{sort_column} #{sort_direction}, created_at desc").page(params[:page]).per(per_page_items)
else
redirect_to root_path
end
rescue Exception => e
redirect_to root_path, :notice => "Something went wrong!"
end
end
route.rb
get "/search" => 'welcome#search_equipments', as: :search_equipments
index.html.erb
The line which is generating the url
<%= search_equipments_path(:category_id => category.id ) %>
You are generating URLs in such a way as to ignore your to_param method. You're explicitly passing a value of only the ID to be used as the :category_id segment of your URLs. If you want to use your to_param-generated ID, then you need to just pass the model to the path helper:
<%= search_equipments_path(category) %>

How to create nested models from API request?

I've a Rails API and I've two models:
class Event < ActiveRecord::Base
belongs_to :category
has_many :event_categories
has_many :events, through: :event_categories
attr_accessible :title, :description, :event_categories_attributes
accepts_nested_attributes_for :event_categories
end
and
class EventCategory < ActiveRecord::Base
belongs_to :event
belongs_to :category
attr_accessible :category_id, :event_id, :principal
validates :event, :presence => true
validates :category, :presence => true
validates_uniqueness_of :event_id, :scope => :category_id
end
In a first moment, EventCategory didn't exist so I created Event resources sending params like event[title]='event1', event[description] = 'blablbla' thought POST REST request.
My API EventsController was like this (I haven't a new method because I don't need views):
def create
#event = Event.create(params[:event])
if #event
respond_with #event
else
respond_with nil, location: nil, status: 404
end
end
This way worked correctly for me. Now, with the new EventCategory model I don't know how I could create EventCategories models at the same time.
I've trying this... but it doesn't work:
def create
#event = Event.new(params[:event])
#event.event_categories.build
if #event.save
respond_with #event
else
respond_with nil, location: nil, status: 404
end
end
Rails told me:
{
"event_categories.event": [
"can't be blank"
],
"event_categories.category": [
"can't be blank"
]
}
I send the category_id like this:
event[event_categories_attributes][0][category_id] = 2
Any ideas?
In your create action, instead of this:
#event.event_categories.build
Try this:
#event.event_categories = EventCategory.new do |ec|
ec.event = #event
ec.category = the_cattegory_you_want_to_specify
# You need both of these as you are validating the presence of event AND category
end

Rails Active Record Query and JSON

I have the following models in my Rails application:
class Transaction
belongs_to :category
has_one :group, :through => :category
class Category
belongs_to :group
has_many :transactions
class Group
has_many :categories
has_many :transactions, :through => :category
In my controller is the following:
#transactions = Transaction.includes(:category, :group).group("groups.id").sum("amount")
respond_to do |format|
format.json{
render :json => JSON.generate(#transactions.as_json(:include => [:category, :group]))
}
end
This produces the following json (A):
{"1":2000,"3":5000,"2":1000}
However, my goal is to produce something like this (B):
[{"group_id":1,"amount":2000},{"group_id":3,"amount":5000},{"group_id":2,"amount":1000}]
Any help on how I can go from A to B would be greatly appreciated.
Try:
def user_transactions
#transactions = Transaction.includes(:category, :group).group("groups.id").sum("amount")
respond_to do |format|
format.html
format.json do
render :json => custom_json_for(#transactions)
end
end
end
private
def custom_json_for(value)
list = value.map do |k,v|
{ :group_id => k,
:amount=> v
}
end
list.to_json
end
If you're doing a lot a JSON-serialization, I would recommend you to take a look at ActiveModelSerializers:
https://github.com/rails-api/active_model_serializers

"undefined method `metadata' for.." rails

Hi i have this problem running the spec file.
this is the reparator model:
class Reparator < User
include Mongoid::Document
include Mongoid::Timestamps
field :private_reparator, :type => Boolean, :default => true
field :brand_name, :type => String
field :year_of_experience, :type => Integer, :default => 1
has_many :reparations
has_many :skills
validates_presence_of :skills, :year_of_experience
validates :year_of_experience, :numericality => {:greater_than_or_equal_to => 0}
end
This is the skill model:
class Skill
include Mongoid::Document
field :name, :type => String
belongs_to :reparator
validates_presence_of :name
validates_uniqueness_of :name
end
This is the controller:
class ReparatorsController < ApplicationController
respond_to :json
def index
#reparators = Reparator.all
respond_with #reparators
end
def show
#reparator = Reparator.find(params[:id])
respond_with #reparator
end
def create
#reparator = Reparator.new(params[:reparator])
#reparator.skills = params[:skills]
if #reparator.save
respond_with #reparator
else
respond_with #reparator.errors
end
end
def update
#reparator = Reparator.find(params[:id])
if #reparator.update_attributes(params[:reparator])
respond_with #reparator
else
respond_with #reparator.errors
end
end
def destroy
#reparator = Reparator.find(params[:id])
#reparator.destroy
respond_with "Correctly destroyed"
end
end
And this is the spec file for this controller (i'll just put the test that does't pass):
it "Should create an reparator" do
valid_skills = [FactoryGirl.create(:skill).id, FactoryGirl.create(:skill).id]
valid_attributes = {:name => "Vianello",
:email => "maremma#gmail.com",
:address => "viale ciccio",
:private_reparator => true
}
post :create, :reparator => valid_attributes, :skills => valid_skills
assigns(:reparator).should be_a Reparator
assigns(:reparator).should be_persisted
end
And this is the skill Factory girl:
FactoryGirl.define do
factory :skill do
sequence(:name) {|n| "skill#{n}"}
end
end
I think there is a typo in your spec. post :create, :reparator => valid_attributes, :skills => skills_ttributes should be post :create, :reparator => valid_attributes, :skills => skills_attributes instead.
The bad line is this one
#reparator.skills = params[:skills]
params[:skills] is an array of strings (the ids that have been passed) but the skills= method expects to be given actual instances of Skill and so blows up.
As well as skills=, mongoid also gives you a skill_ids= method which allows you to change which objects are associated by just assigning an array of ids. Alternatively, load the skills object your self and then to #reparator.skills = skills

'distance' field gets dropped when using Geokit with acts_as_mappable :through

We know that the distance field gets dropped when using the Geokit gem in Rails with acts_as_mappable :through model class. I wonder if there's a way to work around this to get the distance field back. I tried to follow the monkey-patching example over here:
http://www.sobyteme.com/news/2010/05/13/computers/2010/06/25/geokit-acts_as_mappable-through-with-distance-attribute/
but it didn't work for me.
Well, Steve's suggestion over on his site was accurate, I was missing calling sort_by_distance_from after doing the find. So credit goes to him for this answer.
I'm on Rails v3.0.7. Here's my code:
class Office < ActiveRecord::Base
has_many :users
acts_as_mappable :default_units => :miles,
:default_formula => :sphere,
:lat_column_name => :latitude,
:lng_column_name => :longitude
end
class User < ActiveRecord::Base
belongs_to :office
acts_as_mappable :through => :office
end
users_controller.rb:
# Monkey patching to include the 'distance' attribute
module Geokit
module Mappable
def to_lat_lng
return self if instance_of?(Geokit::LatLng) || instance_of?(Geokit::GeoLoc)
return LatLng.new(self.office.send(self.office.class.lat_column_name),
self.office.send(self.office.class.lng_column_name)) if self.class.respond_to?(:acts_as_mappable)
nil
end
end
end
class UsersController < ApplicationController
def location
#lat = params[:lat].to_f
#long = params[:long].to_f
#origin = [#lat, #long]
#users = User.find(:all,
:origin => #origin,
:conditions => "distance < 3")
# We have to add this to get the 'distance' field
#users.sort_by_distance_from(#origin)
respond_to do |format|
format.html
format.xml { render :xml => #users.to_xml(:methods => :distance)}
format.json { render :json => #users.to_json(:methods => :distance)}
end
end
...
end

Resources