How to insert rows in a many-to-many relationship - ruby-on-rails

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

Related

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!

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

How do I create a numerical rating system in Rails?

I'd like to create a numerical rating system in rails where users can rate a post from 1 - 10.
I've looked on Google but I only find outdated tutorials and star rating gems which simply don't do the job for me.
Perhaps someone can point me to a gem that can help me achieve this?
Ruby Toolbox lists several, although most are DOA. Mongoid_ratings seemed to be the most recently updated, although you may not want to go the Mongo route.
https://www.ruby-toolbox.com/categories/rails_ratings
I would suggest building from scratch. Heres a quick (and probably non-functional/non-secure) hack that might help get you started:
Routes
resources :articles do
resources :ratings
end
Models
class Article < ActiveRecord::Base
has_many :ratings, :dependent => :destroy
end
class Rating < ActiveRecord::Base
belongs_to :article
validates_presence_of :article
validates_inclusion_of :value, :in => 1..10
end
Controllers
class RatingsController < ApplicationController
before_filter :set_article
def create
#rating = #article.ratings.new :value => params[:value]
if #rating.save
redirect_to article_ratings_path(#article), :notice => "Rating successful."
else
redirect_to article_ratings_path(#article), :notice => "Something went wrong."
end
end
def update
#rating = Rating.find(params[:id])
#rating.update_attribute :value, params[:value]
end
private
def set_article
#article = Article.find(parms[:article_id])
end
end
In an article view somewhere:
form_for [#article,#rating] do |f|
f.select("rating", "value", (1..10))
f.submit "Rate this Article"
end
Have a look at the Letsrate gem: https://github.com/muratguzel/letsrate
Works great for me.

Has_many through - One Sided Uniqueness in the database

I have a three models:
class Feed < ActiveRecord::Base
has_many :filters, :dependent => :destroy
has_many :keywords, :through => :filters, :uniq => true
end
class Filter < ActiveRecord::Base
belongs_to :feed
belongs_to :keyword
validates_uniqueness_of :keyword_id, :scope => :feed_id
end
class Keyword < ActiveRecord::Base
has_many :filters, :dependent => :destroy
has_many :feeds, :through => :filters
end
What I want is to have only unique entries in the database for keywords. For example, if two feeds both have a keyword 'hello', there should be two filters (one for each feed) both pointing to the same keyword.
What I am having trouble with is the controller code. Perhaps I am looking for too simple a solution, but I figure there must be an easy way to do this. This is what I have in my create action so far:
def create
#feed = Feed.find(params[:feed_id])
#keyword = #feed.keywords.create(params[:keyword])
redirect_to feed_keywords_path(#feed), notice: 'Keyword added successfully.'
end
With this controller code, the previous example would result in a duplicate keyword in the database, one for each feed/filter. Is there a straight-forward solution to this or do I need to do a check beforehand to see if there is already a keyword and in that case just create the filter?
Use a dynamic finder find_or_create_by :
def create
#feed = Feed.find(params[:feed_id])
#keyword = Keyword.find_or_create_by_keyword(params[:keyword]) # I assume here that you have a column 'keyword' in your 'keywords' table
#feed.keywords << #keyword unless #feed.keywords.all.include?(#keyword)
redirect_to feed_keywords_path(#feed), notice: 'Keyword added successfully.'
end

Updating join table field

class Job < ActiveRecord::Base
has_many :employments, :dependent => :destroy
has_many :users, :through => :employments
class User < ActiveRecord::Base
has_many :employments
has_many :jobs, :through => :employments
class Employment < ActiveRecord::Base
belongs_to :job
belongs_to :user # Employment has an extra attribute of confirmed ( values are 1 or 0)
In my view i am trying to update the confirmed fied from 0 to 1 on user click.
<%= link_to "Confirm Job", :action => :confirmjob, :id => job.id %>
In my job Controller I have
def confirmjob
#job = Job.find(params[:id])
#job.employments.update_attributes(:confirmed, 1)
flash[:notice] = "Job Confirmed"
redirect_to :dashboard
end
I am sure this is all wrong but I seem to be guessing when it comes to has_many: through.
How would I do update the confirmed field in a joined table?
I think that a job is assigned to a user by the employment. Thus, updating all employments is not a good idea, as Joel suggests. I would recommend this:
class Employment
def self.confirm!(job)
employment = Employment.find(:first, :conditions => { :job_id => job.id } )
employment.update_attribute(:confirmed, true)
end
end
from your controller
#job = Job.find(params[:id])
Employment.confirm!(#job)
This implies that one job can only be taken by one user.
Here is a stab at it (not tested):
def confirmjob
#job = Job.find(params[:id])
#jobs.employments.each do |e|
e.update_attributes({:confirmed => 1})
end
flash[:notice] = "Job Confirmed"
redirect_to :dashboard
end

Resources