Rails 3 uniqueness validation with scope on polymorphic table - ruby-on-rails-3

I've got the following setup:
class Vote < ActiveRecord::Base
belongs_to :voteable, :polymorphic => :true, :counter_cache => true
end
class Proposition < ActiveRecord::Base
has_many :votes, :as => :voteable
end
class Winner < ActiveRecord::Base
has_many :votes, :as => :voteable
end
The Vote table looks like this:
t.string "ip_address"
t.integer "voteable_id"
t.string "voteable_type"
I want to validate the following. A user with a given ip_address can only vote on 1 proposition. So the combination of ip_address, voteable_id and voteable_type needs to be unique.
How can i achieve this with a "simple" validation rule?

To guarantee uniqueness you have to add unique index to your DB
If you don't have important data yet you can do it inside migration with add_index
add_index(:votes, [:ip_address, :voteable_id, voteable_type], :unique => true, :name => 'allowed_one_vote')
in case you already have some data it can be done with SQL and it depends on your DBMS

Add a scope to a unique :ip_address validation
class Vote < ActiveRecord::Base
# ...
validates :ip_address, uniqueness: { scope: [:voteable_type, :voteable_id]}
end

Related

Reverse association for belongs_to - has_one

One thing about active record that has been confusing me (I'm still kinda new with rails). I'm doing a migration like so:
def up
change_table :slide do |t|
t.references => :slideable, :polymorphic => true
end
end
and then I'll modify my models thus:
class Slide < BaseModel
...
belongs_to :slideable, :polymorphic=>true
end
class Painting < BaseModel
...
has_one :slide, :as => :slideable
end
class Paper < BaseModel
...
has_one :slide, :as => :slideable
end
Do I also have to do a migration for the has_one relationships on Painting and Paper in order to be able to use both sides of the association?
slide.painting.name
slide.paper.title
painting.slide.name
paper.slide.name
No, has_one does not affect your database. belongs_to is what will actually create a foreign key field in your table, that is why you need a migration.

Rails model can't access belongs_to model even though foreign key exists

I have a Model called Challenge that is created by a User. It has a Difficulty associated with it as well. When I create the Challenge and hand it the Author (User) and Difficulty, the Difficulty associate works, but the Author (User) one doesn't. The weirdest part is, when you look at the Challenge object, it shows the Author key its associated with.
challenge = Challenge.first
challenge.author (prints Nil) #Doesn't even run a Query
When I create a Challenge using the following code, the user_id is Nil.
user = User.find(1)
diff = Difficulty.find(1)
Challenge.create(:author => user, :difficulty => diff, :title => "My Challenge")
When I create a Challenge using this code, the User gets the relation to the Challenge and the Challenge gets the user_id of the User. But you can only go from User to Challenge. Challenge to User returns Nil.
user = User.find(1)
diff = Difficulty.find(1)
chall = Challenge.create(:difficulty => diff, :title => "My Challenge")
user.authored_challenges << chall
Here are my Models and Tables
# Models
class User < ActiveRecord::Base
has_many :authored_challenges, :class_name => "Challenge"
attr_accessible :display_name, :authored_challenges
end
class Reward < ActiveRecord::Base
has_many :challenges
attr_accessible :name, :value, :challenges
end
class Challenge < ActiveRecord::Base
belongs_to :reward
belongs_to :author, :class_name => "User"
attr_accessible :title, :description, :reward, :author
end
# Tables
create_table :users do |t|
t.string :display_name
t.timestamps
end
create_table :rewards do |t|
t.string :name
t.float :value
t.timestamps
end
create_table :challenges do |t|
t.integer :user_id
t.integer :reward_id
t.string :title
t.string :description
t.timestamps
end
According to: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html the foreign key should be:
:foreign_key => :author_id
Don't forget to add the field. Good Luck!
Have you tried:
belongs_to :author, :class_name => "User", :foreign_key => :user_id
From the Rails documentation:
By convention, Rails assumes that the column used to hold the foreign key on this model is the name of the association with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly
The example given in the docs is very similar to the one you have:
class Order < ActiveRecord::Base
belongs_to :customer, :class_name => "Patron", :foreign_key => "patron_id"
end

Defining table name for has_many self joins in rails?

I wish to set up nested comments, and want to use self-join to set this up.
class Comment < ActiveRecord::Base
has_many :children, :class_name => 'Comment'
#...
end
Now, what sql table structure would I use to set up the has_many self-join?
I'm assuming something like this:
comment_to_comments:
parent_id integer
child_id integer
How do I tell rails to use this table? How do I tell rails that parent_id is the foreign key to reach the parent and the child_id is the foreign key to reach the child?
create_table :comments do |t|
t.integer :parent_id
end
class Comment < ActiveRecord::Base
has_many :children, :class_name => "Comment", :foreign_key => :parent_id
belongs_to :parent, :class_name => "Comment"
end
I suggest you use plugin to implement this feature.like awesome_nested_set or acts_as_tree.

Mixing has_one and has_and_belongs_to_many associations

I'm trying to build a database of urls(links). I have a Category model that has and belongs to many Links.
Here's the migration I ran:
class CreateLinksCategories < ActiveRecord::Migration
def self.up
create_table :links_categories, :id => false do |t|
t.references :link
t.references :category
end
end
def self.down
drop_table :links_categories
end
end
Here's the Link model:
class Link < ActiveRecord::Base
validates :path, :presence => true, :format => { :with => /^(#{URI::regexp(%w(http
https))})$|^$/ }
validates :name, :presence => true
has_one :category
end
Here's the category model:
class Category < ActiveRecord::Base
has_and_belongs_to_many :links
end
And here's the error the console kicked back when I tried to associate the first link with the first category:
>>link = Link.first
=> #<Link id: 1, path: "http://www.yahoo.com", created_at: "2011-01-10...
>>category = Category.first
=> #<category id : 1, name: "News Site", created_at: "2011-01-11...
>>link.category << category
=> ActiveRecord::StatementInvalid: SQLite3::Exception: no such column :
categories.link_id:
Are my associations wrong or am I missing something in the database? I expected it to find the links_categories table. Any help is appreciated.
Why are you using HABTM here at all? Just put a belongs_to :category on Link, and a has_many :links on Category. Then in the db, you don't need a join table at all, just a :category_id on the links table.
But, if you do want a HABTM here, from a quick glance, the first thing I noticed is that your join table is named incorrectly -- it should be alphabetical, categories_links.
The second thing is that you can't mix has_one and has_and_belongs_to_many. HABTM means just that -- A has many of B and A belongs to many of B; this relationship implies that the opposite must also be true -- B has many of A and B belongs to many of A. If links HABTM cateogries, then categories must HABTM links.
See http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many

multiple joins using activerecord in rails

I'm building a small twitter style microblogging service where users can follow other users and get a feed of their messages
I have the following models:
class Follow < ActiveRecord::Base
belongs_to :follower, :class_name => "User"
belongs_to :followee, :class_name => "User"
end
class User < ActiveRecord::Base
has_many :follows, :foreign_key => 'follower_id',
:class_name => 'Follow'
has_many :followers, :through => :follows
has_many :followed, :foreign_key => 'followee_id',
:class_name => 'Follow'
has_many :followees, :through => :followed
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
To get a feed for the current user, I want to perform the following SQL query:
SELECT * FROM follows JOIN users JOIN messages WHERE follows.follower_id = current_user.id AND follows.followee_id = users.id AND users.id = messages.user_id;
What is the correct ActiveRecord way of doing this?
Not sure what you're looking for, but here is my suggestion:
I assume that you have other purposes for that Follow class, otherwise I don't see the purpose of it.
The "correct way" (i.e. my completely subjective way) to do it would actually be something like this:
class User < ActiveRecord::Base
has_and_belongs_to_many :followers, :foreign_key => 'followed_id',
:class_name => 'User', :association_foreign_key => 'follower_id',
:include => [:messages]
has_and_belongs_to_many :follows, :foreign_key => 'follower_id',
:class_name => 'User', :association_foreign_key => 'followed_id'
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
Then create the following table:
create_table :users_users, :id => false do |t|
t.integer :followed_id
t.integer :follower_id
end
And you're set:
followed = User.find :first
follower = User.find :last
followed.followers << follower
followed.followers.first.messages
followed.followers.first.followers.first.messages # etc...
But from what I make it, you want to show all the messages from all the followers at the same time.
This should be possible to achieve by adding
has_and_belongs_to_many :followed_messages, :foreign_key => 'follower_id',
:class_name => 'Message', :association_foreign_key => 'followed_id'
to the User class, but I don't know how correct that way would be. Or it might be possible to achieve with association extensions but there I can't really give any examples.
Update:
By changing the :class_name, it will associate it with the Message.id, didn't think about that so it will not be correct in this way.
So the only "nice" option is to go through the User class like in the first example.
The only other options I can see is either the association extensions (which I can't give you an example for) or perhaps using a finder statement.
has_many :followed_messages, :class_name => 'Message',
:finder_sql => 'select * from messages where user_id in(select followed_id from users_users where follower_id = #{id})'
You probably have to customize that sql statement to get everything to work, but at least you should get the picture :)
Keijro's arrangement would work better, though if you need the Follow table, then you can execute the SQL query you specified as follows:
Follow.all(:joins => { :messages, :users }, :conditions => { "follows.follower_id" => current_user.id, "follows.followee_id" => "users.id", "users.id" => "messages.user_id"} )