Include both joined tables in Rails JSON API call - ruby-on-rails

I am getting the following Active Record Association error when trying to join two tables (with a polymorphic relationship) and include all data from both tables in a JSON API response:
Association named 'categories' was not found; perhaps you misspelled it?
Here is the controller action that I am trying to call:
def index
#items = Item.includes(:categories)
respond_to do |format|
format.json { render :json => #items.to_json }
end
end
And here are the two models that I am trying to join:
class Category < ActiveRecord::Base
attr_accessible :name
has_many :items, :as => :linkable
end
class Item < ActiveRecord::Base
attr_accessible :due_date, :linkable_id, :linkable_type, ...
belongs_to :linkable, :polymorphic => true, :counter_cache => true
end
Specifically, I want to return each Item in the database along with its Category. I have tried everything that I can think of. Any help will be greatly appreciated.

Have you tried :
def index
#items = Item.includes(:linkable).where(:linkable_type => 'Category')
respond_to do |format|
format.json { render :json => #items.to_json(include: :linkable) }
end
end
The name of your association is actually :linkable for the Item model, and not :categories (especially because it's a belongs_to so it would be :category).

Related

Ruby On Rails Pagination and delete :through association

I am running into a strange situation, considering the following models:
class Collection < ActiveRecord::Base
attr_accessible :name, :season, :year
has_many :collection_items_assocs
has_many :items, :through => :collection_items_assocs
end
class Item < ActiveRecord::Base
attr_accessible :name, :reference, :item_type_id
has_many :pictures
has_one :item_type
end
class CollectionItemsAssoc < ActiveRecord::Base
attr_accessible :collection_id, :item_id
belongs_to :item
belongs_to :collection
end
I can successfully retrieve Items associated to a Collection with the following code:
# GET /collections/1
# GET /collections/1.json
def show
#collection = Collection.find(params[:id])
#collection.items = Collection.find(params[:id]).items
respond_to do |format|
format.json { render json: #collection.to_json(:include => {:items => #collection}) }
end
end
But when I try to include pagination (for items) like that
# GET /collections/1
# GET /collections/1.json
def show
#collection = Collection.find(params[:id])
**#collection.items = Collection.find(params[:id]).items.paginate(:page => params[:page],:per_page =>1)**
respond_to do |format|
format.json { render json: #collection.to_json(:include => {:items => #collection}) }
end
end
It works for the following call
/retailapp/collections/1?format=json&**page=1**
Then if I call
/retailapp/collections/1?format=json&**page=2**
the records in the association table CollectionItemsAssoc are deleted
I really don't get it
Thanks for your help
The problem is the code to fetch the items
#collection.items = Collection.find(params[:id]).items
it assigned the fetched items to current collection object.
you need to change the response to support the pagination on associate objects
  def show
   #collection = Collection.find(params[:id])
respond_to do |format|
format.json {
json_hash = #collection.as_json
json_hash[:items] = #collection.items.paginate(:page => params[:page],:per_page =>1).as_json
render json: json_hash.to_json
}
end
Additionally you can overwrite to_json method inside Collection model.

Validate many-to-many association

posts 1--* post_tags *--1 tags
A post has many tags and a tag has many posts. They are related through the post_tags table.
post.rb
class Post < ActiveRecord::Base
attr_accressible :tag_ids
has_many :post_tags
has_many :tags, :through => :post_tags
end
tag.rb
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, :through => :post_tags
end
post_tag.rb
class PostTag < ActiveRecord::Base
belongs_to :post
belongs_To :tag
end
posts_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(params[:post])
#post.save ? redirect_to(:action => 'index') : render(:action => 'new')
end
end
The tags table is a catalog and I just want the user to select the appropriate ones for the post.
So in my view I have a multiple select:
new.html.erb
post_form.collection_select(:tag_ids, #tags, nil, nil, {}, { :multiple => true })
This works but the problem is when I send invalid ids I get this error
ActiveRecord::RecordNotFound in Posts#create
Couldn't find all Tags with IDs (1, 200) (found 1 results, but was looking for 2)
Tag with id 1 exists but tag with id 200 doesn't.
Any ideas?
Got your problem, when you send in some invalid tag_id say, 200 the Rails will first search whether the Tag with ID 200 actually exist or not. So the best way to tackle this problem is to use a before filter where you make sure that you get the correct or valid ids. And then you can simply do an assignment which would assign the tag_ids to the post....You can do something like this
before_filter :find_tags_from_ids
def find_tags_from_ids
#tags = Tag.where(:id => params[:post][:tag_ids]
end
def create
#post = Post.new(params[:post])
#post.tags << #tags
#post.save ? redirect_to(:action => 'index') : render(:action => 'new')
end
This would solve your problem and hopefully you won't get an exception this time..

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

Rails: Active Record Question

I have the following models:
require 'books_projects.rb'
class Project < ActiveRecord::Base
has_many :book_to_projects
has_many :books, :through => :book_to_projects
end
require 'books_projects.rb'
class Book < ActiveRecord::Base
has_many :book_to_projects
has_many :projects, :through => :book_to_projects
end
books_projects.rb:
class BookToProject < ActiveRecord::Base
set_table_name "books_projects"
belongs_to :book
belongs_to :project
end
In my projects controller, I am trying to do this:
#projects = Project.find(:all, :include => [:books])
My hope is to get a nested data structure back that looks something like:
projects: [
{
..,
books: [
{
..
},
{
..
}
]
}
]
This doesn't work. How can I achieve this?
Also, does the above model relationship call for the use of has_and_belongs_to_many in project and book instead of specifying the join table?
Thanks in advance!
Edit 1:
I don't have a view. This acts like a REST service only. I am doing the following:
def index
#projects = Project.find(:all, :include => [:books])
respond_to do |format|
format.xml { render :xml => #projects }
format.json { render :json => #projects}
end
end
This does not produce the output that I'm looking for (mentioned above).
You code does exactly what you expect it to do. The problem is in verifying that.
Try this:
project = Project.find(:first)
project.books.loaded?
would return false
project = Project.find(:first, :include => [:books])
project.books.loaded?
would return true :)
The eager-loaded associations do not normally show up when you inspect the object.
So turns out that the following works:
def index
#projects = Project.find(:all)
respond_to do |format|
format.json { render :json => #projects.to_json(:include => [:books]) }
end
end
Can someone tell me why this works and the one before didn't?

Error creating a new survey as the associated model(s) are invalid (questions)

I have a survey as part of an application I'm building. The user can create a survey and specify questions dynamically (can have as many as they want), so I've used an associated model with:
#survey.rb
has_many :survey_questions, :dependent => :destroy
has_many :survey_answers, :dependent => :destroy
after_update :save_survey_questions
validates_associated :survey_questions
def save_survey_questions
survey_questions.each do |t|
if t.should_destroy?
t.destroy
else
t.save(false)
end
end
end
def survey_question_attributes=(survey_question_attributes)
survey_question_attributes.each do |attributes|
if attributes[:id].blank?
survey_questions.build(attributes)
else
survey_question = survey_questions.detect { |e| e.id == attributes[:id].to_i }
survey_question.attributes = attributes
end
end
end
#surveys_controller.rb
def new
#survey = Survey.new
if(#survey.survey_questions.empty?)
#survey.survey_questions.build
end
respond_to do |format|
format.html # new.html.erb
end
end
def create
#survey = Survey.new(params[:survey])
respond_to do |format|
if #survey.save
format.html { redirect_to(survey_path(:id => #survey)) }
else
format.html { render :action => "new" }
end
end
end
#survey_question.rb
class SurveyQuestion < ActiveRecord::Base
belongs_to :survey
attr_accessor :should_destroy
def should_destroy?
should_destroy.to_i == 1
end
validates_presence_of :question, :survey_id
end
The problem is when I submit I get an error on the questions:
#errors={"survey_questions"=>["is invalid", "is invalid", "is invalid"]}
I believe it is because the survey_id I have linking surveys to survey_questions is not being filled in.
Any ideas how I can overcome this?
If I create the survey with no questions, then add them afterwards via edit, then it works perfectly.
I'm pretty sure that accepts_nested_attributes can help you a lot, there you'll find some examples building the associated objects wich seems to be your problem (since the survey_id in survey_questions is not being filled in), basically you should define in your models something like:
class Survey < ActiveRecord::Base
has_many :survey_questions
accepts_nested_attributes_for :survey_questions, :allow_destroy => true
...
end
It will handle all the SurveyQuestions validations through Survey.
Your code looks close to the standard --> http://railscasts.com/episodes/75-complex-forms-part-3
Are you sure you are getting the question parameter back correctly? I only ask because that is the other thing you are validating against and you don't have the form code in there so I can't see what's coming back to the controller.
OK,
I've managed to fix it and it was a really silly mistake on my part.
In survey_question.rb I had the line :
validates_presence_of :question, :survey_id
However, rails automatically deals with survey_id because of the has_many belongs_to relationship!
So this should be validates_presence_of :question
I also in the process of finding this out upgraded rails to 2.3.4 and started using:
accepts_nested_attributes_for :survey_questions, :allow_destroy => true
which dealt with all the attributes etc.

Resources