validate uniqueness of field in scope - ruby-on-rails

I have a user model which has a polymorphic relationship to teachers, students, and admin. These three types of users each belong to a school. I would like to have it so the username of the user is unique within a school. How would I write the validations to accomplish this?
Here is what my models look like:
class User < ActiveRecord::Base
belongs_to :profileable, :polymorphic => true
delegate :school, :to => :profileable
end
class Student < ActiveRecord::Base
belongs_to :school
has_one :user, :as => :profileable
delegate :name, :username, :to => :user
end
class Teacher < ActiveRecord::Base
belongs_to :school
has_one :user, :as => :profileable
delegate :name, :username, :to => :user
end
class Admin < ActiveRecord::Base
belongs_to :school
has_one :user, :as => :profileable
delegate :name, :username, :to => :user
end

I'm quite sure you will need to use a custom validator for this. The delegated attribute will not be usable in the User model. What you could do is also include the school_id in the User method and set it using before_validate every time. Then you'd be able to use the "simple" uniqueness validator:
validates :username, :uniqueness => {:scope => :school_id}
However, a custom validator joining the school_id of the profileable parent would probably be a cleaner way to go.

Related

Rails - how to make association between these models (not based on "id", but on "email")

I have these models that obviously have more attributes, but for simplicity I kept them just like this:
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "subs"
end
class Subscription < ActiveRecord::Base
belongs_to :plan
belongs_to :user
end
In the table subscriptions, there's a column called email. This column points to the table users where matches an email address of a single user (the column email is in both tables unique).
I would need to create an association between these two models based on the email value. But when I try to run this query (and to get all subscription for the currently sign in user):
<%= current_user.subs.inspect %>
I get this error message:
uninitialized constant User::subs
I'd like to ask you guys for helping me with this association.
Thanks
uninitialized constant User::subs
This code
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "subs"
end
should be like this
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "Subscription"
end
When you are using a class_name option with the associations,it should point to the respected model classname(in your case it is Subscription not subs).Since there is no model with the classname subs,it throws that exception.
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "Subscription", :primary_key => :email
end
class Subscription < ActiveRecord::Base
belongs_to :plan
belongs_to :user, :foreign_key => :email, :class_name => "Subscription", :primary_key => :email
end
You have to set the correct Class name and also you have to set the primary key.

Rails : Displaying associated model validations

I have got three models
class RateCard < ActiveRecord::Base
validate :name, :presence => true, :uniqueness => true
has_many :rate_card_countries, :dependent => :destroy
has_many :rate_card_details, :dependent => :destroy
has_many :countries, :through => :rate_card_countries
end
class RateCardCountry < ActiveRecord::Base
validates :country_id, :presence => true, :uniqueness => true
validates :rate_card_id, :presence => true
belongs_to :rate_card
belongs_to :country
end
class Country < Geography
has_one :rate_card
has_one :rate_card_country
end
In rate_cards_controller i want to create/update rate_cards such that one country should have one rate_card..
For that i have added uniqueness validation in RateCardCountry Model.
And NOw i want to display the error in rate_card_controller while creating/updating rate_cards..
Needed help?
If I understand your intention correctly, you are trying to build a one-to-many relationship between RateCard and Country. In other words- a country will have only one RateCard, and a RateCard can belong to many countries.
Assuming that's the case, you really don't need the RateCardCountry model (which will be useful if you wanted it to be a many-to-many relationship).
You will need to have:
class RateCard < ActiveRecord::Base
validate :name, :presence => true, :uniqueness => true
belongs_to :rate_card
end
And make sure you have county_id foreign key in the RateCard table.
and then:
class Country < ActiveRecord::Base
has_one :rate_card
end
Also, it seems that right now you have:
class Country < Geography
I am not sure if you are subclassing from a Geography class, as you have not provided the rest of the code.
Hope that helps.

Serialize delegate in rails

I have 3 models in Rails: User, UserProfile and Post
Something like this:
class Post < ActiveRecord::Base
attr_accessible :content, :post_type
belongs_to :user
delegate :fullname, :to => :user, :prefix => "author"
end
class User < ActiveRecord::Base
has_many :posts
has_one :user_info
delegate :fullname, :to => :user_info
end
class UserInfo < ActiveRecord::Base
attr_accessible :avatar, :fullname, :birthday, :nickname, :gender, :about
belongs_to :user
end
Now I use knockout to manage posts at client-side so I have to make my object to json using posts.to_json. These JSON objects don't have attributes fullname. I tried with user.to_json, and these objects don't have that attribute either. So how can I make the delegate serialize to JSON?
Since fullname is a virtual attribute in the sense:
Rails 2:
posts.to_json(:method => %w(fullname))
user.to_json(:method => %w(fullname))
Rails 3:
posts.to_json(:methods => %w(fullname))
user.to_json(:methods => %w(fullname))

how to set unique attribute in a model row

I have a model Competitor like this
class Competitor < ActiveRecord::Base
belongs_to :admin_user
has_many :companies
attr_accessible :admin_user_id, :c1, :c2, :c3, :c4, :c5
validates :admin_user_id, :presence => true
validates_uniqueness_of :admin_user_id, :message => "This user has yet a competitors list"
end
C1, c2,.. are the id of companies. Selected from a drop down list. How can I validate the uniqueness of a row?
(i.e. is not possible to have two or more equals companies for an admin user BUT they can be empties).
You could write your own validation method that would enforce this.
class Competitor < ActiveRecord::Base
belongs_to :admin_user
has_many :companies
attr_accessible :admin_user_id, :c1, :c2, :c3, :c4, :c5
validates :admin_user_id, :presence => true
validates_uniqueness_of :admin_user_id, :message => "This user has yet a competitors list"
validate :check_companies
def check_companies
#[do your checks]
end
end

Validation on associtions in rails with roles

So I have two ActiveRecord classes
class User < ActiveRecord::Base
has_many :buyer_deals, :class_name => "Deal", :foreign_key => :buyer_id
has_many :seller_deals, :class_name => "Deal", :foreign_key => :seller_id
validates_presence_of :name # THIS SHOULD ONLY BE RUN IF USER IS A SELLER
# IN THE DEAL
validates_presence_of :phone # THIS SHOULD ONLY BE RUN IF USER IS A BUYER
# IN THE DEAL
end
class Deal < ActiveRecord::Base
belongs_to :seller, :class_name => 'User'
belongs_to :buyer, :class_name => 'User'
validates_associated :seller
validates_associated :buyer
end
What I want to do is create a new deal with.
Deal.create(A NICE STRUCT WITH SELLER AND BUYER)
However I only want to run the name validation if the relation from the deal is a seller and the phone if the the relation from the deal is a seller, is this possible in rails, does not seem to find anything in the documentation.
You should be able to do this by adding a condition to you validation.
So, your User class would wind up looking something like...
class User < ActiveRecord::Base
has_many :buyer_deals, :class_name => "Deal", :foreign_key => :buyer_id
has_many :seller_deals, :class_name => "Deal", :foreign_key => :seller_id
validates_presence_of :name, :if => :has_an_active_seller_deal?
validates_presence_of :phone, :if => :has_an_active_buyer_deal?
def has_active_seller_deals?
seller_deals.count > 0
end
def has_active_buyer_deals?
buyer_deals.count > 0
end
end
An alternative to this would be to simply require all users to have a name and phone number on file at all times (no conditional validation), and only reveal it to other users with which they had active deals, and not as part of a user's public profile, thereby protecting the user's privacy when possible. This would probably be simpler.
You could put the validations in a callback:
before_save :check_user_type
private
def check_user_type
user_type = self.responds_to?(seller_id) ? :seller : :buyer
if user_type == :seller
validates_presence_of :name
else
validates_presence_of :phone
end

Resources