Validate many-to-many association - ruby-on-rails

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..

Related

Include both joined tables in Rails JSON API call

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).

Rails: destroy in wrong order

I have some problems with destroy action in my Rails app.
In my app I have model UserVotes, which allows users vote for each other. For example: John votes for other users in the order:
Vote for User_1
Vote for User_2
Vote for User_3
When John wants to delete his vote for User_3 he is deleting vote for User_1, after retrying he's deleting vote for User_2, and only after two attempts he's deleting vote for User_3
user_votes_controller:
class UserVotesController < ApplicationController
def destroy
#user_vote = UserVote.find_by(params[:recipient_uid])
#user_vote.destroy
redirect_to root_url
flash[:warning] = 'Deleted'
end
end
view:
= link_to('Delete vote', user_vote, author_uid: current_user.uid, method: :delete)
I can see couple of issues in the code you have provided.
You seem to be giving recipient_uid in find_by for UserVote, it should be uservote_id.
You are finding the vote to destroy by passing the author id, which cant be the unique key in votes. It will just return you the first entry in votes table.
Due to point 2, you are getting the vote to user 1 first, vote to user 2 next and so on. You will have to explain the structure of user,vote,user_vote a little more, if you want me to provide correct solution.
The Models:
class User < ActiveRecord::Base
has_many :user_votes
has_many :votes, :through => :user_votes
end
class Vote < ActiveRecord::Base
has_many :user_votes
has_many :users, :through => :user_votes
end
class UserVote < ActiveRecord::Base
belongs_to :author, class_name: "User", :foreign_key => :author_uid
belongs_to :recipient, class_name: "User", :foreign_key => :recipient_uid
belongs_to :vote
end
The view
= link_to('Delete vote', user_vote_path(user_vote), method: :delete)
The Controller
class UserVotesController < ApplicationController
def destroy
#user_vote = UserVote.find_by(params[:id])
#vote = #user_vote.vote
#vote.destroy
#user_vote.destroy
redirect_to root_url
flash[:warning] = 'Deleted'
end
end
Solution was quite simple. I just rewrote action destroy in users_votes controller:
class UserVotesController < ApplicationController
def destroy
#user_vote = UserVote.find(params[:id])
#user_vote.destroy
redirect_to root_url
flash[:warning] = 'Deleted'
end
end
Anyway, thanks for participation!

Rails Destroy Dependency not calling destroy function?

Please check my understanding of how recursive destroys work?
I have a blog object that contains a lot of posts. The posts go on to have a newsfeed object that is created every time a post is created. When I delete the blog, the posts are deleted, but the newsfeed objects on the posts are not being deleted, leaving me with 'ghost' newsfeed objects.
models > blog.rb
class Blog < ActiveRecord::Base
attr_accessible :description, :title, :user_id, :cover
belongs_to :user
has_many :posts, :dependent => :destroy
end
models > post.rb
class Post < ActiveRecord::Base
attr_accessible :blog_id, :content_1, :type, :user_id, :media, :picture, :event_id
belongs_to :blog
belongs_to :user
end
So when I call for a blog to be destroyed, it's picking up all the posts and destroying them. That's great! But I have a special piece of custom code in the post controller's destroy function that calls for custom destruction of the newfeeds. That's not being called.
controllers > post_controller.rb
def destroy
#post = Post.find(params[:id])
# Delete Feed on the creation of the post
if Feed.exists?(:content1 => 'newpost', :content2 => params[:id])
#feeds = Feed.where(:content1 => 'newpost', :content2 => params[:id])
#feeds.each do |feed|
feed.destroy
end
end
#post.destroy
respond_to do |format|
format.html { redirect_to redirect }
format.json { head :no_content }
end
end
That bit of code in the post's destroy function is not being called, so the newfeed objects are not being destroyed. Is my understanding of the dependency destroy functionality wrong?
I specifically would like to avoid creating a belongs_to and has_many relationship between newsfeeds and post objects, because newsfeed objects are triggered by other types of user actions, like friending someone new, or creating a new blog, differentiated by the type of newsfeed it is in the content1 variable.
I would suggest moving the custom Feed-deletion code into your Post model like do:
class Post
before_destroy :cleanup
def cleanup
# Delete Feed on the creation of the post
if Feed.exists?(:content1 => 'newpost', :content2 => id)
#feeds = Feed.where(:content1 => 'newpost', :content2 => id)
#feeds.each do |feed|
feed.destroy
end
end
end
end
Now, if #feeds is empty, then it's probably a problem with the exists? function. But moving this code to this callback function will ensure that anytime a Post is deleted, the associated feeds get deleted.
In your controller, just call #post.destroy as normal, and the rest will take care of itself.

Not showing user during iteration if user already belongs to "group" relationship

I have this relationship where User can create a document(trip) and invite other users to a group that belongs to that document. My relationship indicates that "Group" has a user_id and trip_id column, so for every user I invite, a new Group record will be created in the database.
When I am inviting other users, I only want users who are NOT in the group to appear. Users who are already in the group should not show up, but my view still shows the users.
I've been playing around with <% if !friend.trips.include?(#trip)%>, but I can't seem to get the correct view. The record is being created in the database correctly.
Also, when I am viewing groups/new.html.erb, this is the url http://localhost:3000/groups/new?id=2, where the id is the trip_id.
My question:
Am I using restful convention? That is, should I be using the new method here (as is) or should I be using the index method instead?
How do I iterate through each friend's groups to make sure that none of the group's trip_id is equivalent to #trip.id?
Thanks!
view (/groups/new.html.erb)
<% if !#friends.blank? %>
<% #friends.each do |friend| %>
<% if !friend.trips.include?(#trip)%>
<%= link_to groups_path(:user_id => friend.id, :trip_id => #trip.id),
:method => :post, :action => 'create' do %>
<div id="addfriend_totrip_button_groupsnew">add friend to trip</div>
<% end %>
<% end %>
<% end %>
<% end %>
groups_controller.rb
class GroupsController < ApplicationController
before_filter :authenticate, :only => [:update, :create, :destroy]
def new
#trip = Trip.find(params[:id])
#user = User.find(current_user)
#group = Group.new
#friends = #user.friends.all
end
def create
#trip = Trip.find(params[:trip_id])
#user = User.find(params[:user_id])
#group = Group.create(:user_id => #user.id, :trip_id => #trip.id)
if #group.save
flash[:success] = "Friend added to group."
redirect_to groups_path(:id => #trip.id)
else
flash[:error] = "Could not add friend."
redirect_to root_path
end
end
end
user.rb
class User < ActiveRecord::Base
has_many :trips, :through => :groups
has_many :trips, :dependent => :destroy
has_many :groups
end
trip.rb
class Trip < ActiveRecord::Base
belongs_to :user
belongs_to :traveldeal
has_many :groups
has_many :users, :through => :groups
end
group.rb
class Group < ActiveRecord::Base
belongs_to :trip
belongs_to :user
end
First of all, you have has_many :trips called twice in your User model. I understand you have two different types of User-Trip relationships (one directly, and one through Group), but you can't give both the same name, otherwise one will hide the other. Try defining your User model like this:
class User < ActiveRecord::Base
has_many :group_trips, :through => :groups,
:class_name => "Trip"
has_many :trips, :dependent => :destroy
has_many :groups
def all_trips
Trip.joins(:groups).where({:user_id => self.id} | {:groups => {:user_id => self.id}})
end
end
There's also the problem that you're searching the friend's list of groups for a Trip object. Try changing that line to:
<% if !friend.all_trips.include?(#trip) %>
Or without the new method, something like this should work:
<% if !friend.groups.where(:trip_id => #trip.id).first %>
I don't see anything un-RESTful about your approach. RESTful in general means stateless. I.e. the only thing a response depends on is the HTTP method and the address. So as long as your not keeping state information in, say, the session, you should be following REST.

How to insert rows in a many-to-many relationship

I am having an issue trying to save into an intermediate table. I am new on Rails and I have spent a couple of hours on this but can't make it work, maybe I am doing wrong the whole thing. Any help will be appreciated. =)
The app is a simple book store, where a logged-in user picks books and then create an order.
This error is displayed:
NameError in OrderController#create
uninitialized constant Order::Orderlist
These are my models:
class Book < ActiveRecord::Base
has_many :orderlists
has_many :orders, :through => :orderlists
end
class Order < ActiveRecord::Base
belongs_to :user
has_many :orderlists
has_many :books, :through => :orderlists
end
class OrderList < ActiveRecord::Base
belongs_to :book
belongs_to :order
end
This is my Order controller:
class OrderController < ApplicationController
def add
if session[:user]
book = Book.find(:first, :conditions => ["id = #{params[:id]}"])
if book
session[:list].push(book)
end
redirect_to :controller => "book"
else
redirect_to :controller => "user"
end
end
def create
if session[:user]
#order = Order.new
if #order.save
session[:list].each do |b|
#order.orderlists.create(:book => b) # <-- here is my prob I cant make it work
end
end
end
redirect_to :controller => "book"
end
end
Thnx in advance!
Manuel
Only got time to look at this briefly, I'm afraid, but the first thing I spot is that your has_many relations are called :orderlists. I think that needs to be :order_lists, with an underscore.
This is not directly associated with your question but this query:
book = Book.find(:first, :conditions => ["id = #{params[:id]}"])
...is vulnerable to sql injection. In this case content of params[:id] gets passed to sql without proper escaping. I would suggest changing this line to something like this:
book = Book.find(:first, :conditions => ["id = ?, params[:id]])
Here's explanation: http://wiki.rubyonrails.org/howtos/security/sql_injection
Yes that was one of the problems. Then I could make it work with this line in the 'create' method:
def create
if session[:user]
#order = Order.new
if #order.save
session[:list].each do |b|
OrderList.create(:book => b, :order => #order)
end
end
end
redirect_to :controller => "book"
end
Thanks Chris

Resources