Uniqueness validation with a has_many relationship - ruby-on-rails-3

My model structure is as follows:
class Client < ActiveRecord::Base
has_many :charts
end
class Chart < ActiveRecord::Base
belongs_to :client
has_many :chart_data
end
class ChartDatum < ActiveRecord::Base
belongs_to :chart
end
ChartDatum has an attribute called 'name' which needs to be unique for each client.
I tried using "validates_uniqueness_of :name, :scope => [:chart_id]" but this helped me getting a unique key for a particular chart but not for all charts for a particular client. I am looking for something like "validates_uniqueness_of :name, :scope => [:client_id]" but obviously with the current structure it will not work out.
Could someone please help me?

Since you need unique name for chart_data for each client, you can try writing a custom validation for name something like this :
class ChartDatum < ActiveRecord::Base
belongs_to :chart
validates :name, :uniqueness => true, unless => :unique_for_client?
def unique_for_client?
client = self.chart.client
client.charts.chart_data.pluck(:name).include?(self.name)
end
end

Related

has_many :condition multiple models how to use join here?

I have the following model structure:
Model Visitor
class Visitor < ActiveRecord::Base
has_many: triggers
end
Model Trigger
class Trigger < ActiveRecord::Base
belongs_to :visitor, :inverse_of => :triggers
belongs_to :event, :inverse_of => :triggers
end
Model Event
class Event < ActiveRecord::Base
has_many: triggers
end
I am trying to setup a custom association in Visitor model like so:
has_many: triggers_that_pass_some_condition ,:class_name => "Trigger",
:conditions => ["triggers.some_column >= events.some_column"]
The problem is that it doesn't work .. I am guessing I have to do some kind of join to compare columns of two separate models (that are associated with each other)
I have tried
triggers.some_column >= triggers.event.some_column
That does not work either. Anyone has any suggestions? thanks!
Try the following code..
class Trigger < ActiveRecord::Base
belongs_to :event
belongs_to :visitor
end
# Visitors.rb
has_many :triggers_with_condition, -> { includes(:event).where(some_trigger_column >= event.some_event_column)}, class_name: "Trigger"
Make sure you first add the correct association between Visitor and Trigger in your model setup. From there, you can add a custom association as follows:
class Visitor < ActiveRecord::Base
has_many :approved_triggers, -> { includes(:events).where("events.something = ?", true).references(:events) }, class_name: 'Trigger', inverse_of: :visitor
end
class Trigger < ActiveRecord::Base
belongs_to :visitor, inverse_of :triggers
end
Right now your Trigger class holds no association to a Visitor.
Thanks to the clue from Darpa, I eventually settled on this:
has_many :custom_trigger, {:class_name => "Trigger", :include => :event,
:conditions => ["triggers.some_column >= events.another_column"]}

Validate multiple belongs_to relations

Let's say we have the following data structure (imagine as a loop).
MyModel * -- 1 User 1 -- * Clients 0 -- * MyModel
Thus, the MyModel looks like:
class MyModel < ActiveRecord::Base
belongs_to :client
belongs_to :user
attr_accessible :user_id, :client_id, # ...
validates :user_id, :presence => true
So it can belong to a client but must belong to a user. Also a client must belong to a user.
But how can I assert, that if someone saves a MyModel instance belonging to a client, that the client actually belongs to the user? Do I have to write a custom validator for this or is there any validation shortcut I overlooked?
Solution:
Following p.matsinopoulos' answer, I now did the following. I don't assign the foreign keys to MyModel before saving but rather the objects directly.
# my_model_controller.rb
def create
m = MyModel.create(whitelist_parameters(params[:my_model]))
m.user = current_user
if(params[:my_model][:client_id].present?)
client = Client.find params[:my_model][:client_id]
end
# ...
end
This way I can at first validate if there is a Client with such an ID at all. Then I implemented the custom validator by p.matsinopoulos. It's not tested yet, but since I am now working with the objects, it should do the trick.
If I were you, I would have written a custom validator.
validate :client_belongs_to_user
protected
def client_belongs_to_user
errors[:client] << 'must own the user' if client.present? && client.user != user
end
I would suggest, building on what you have:
class MyModel < ActiveRecord::Base
belongs_to :client
has_one :user, :through => :client
attribute_accessor :client_id
validates :client_id, :presence => true
...
end
class Client < ActiveRecord::Base
has_many :my_models
belongs_to :user
attribute_accessor :user_id
validates :user_id, :presence => true
...
end
class User < ActiveRecord::Base
has_many :clients
...
end

Many to many relationship with metadata stored in the mapping table in activerecord

I have two tables with a many to many relationship, through a third table. In the third table is a piece of data I need to assign when I build the relationships between the two tables, how can I use ActiveRecords build method to assign that?
Here is code to show what I mean:
class Company < Contact
has_many :contact_companies
has_many :people, :through => :contact_companies
accepts_nested_attributes_for :people, :allow_destroy => true
accepts_nested_attributes_for :contact_companies
end
class Person < Contact
has_many :contact_companies
has_many :companies, :through => :contact_companies
accepts_nested_attributes_for :companies, :allow_destroy => true
accepts_nested_attributes_for :contact_companies
end
class ContactCompany < ActiveRecord::Base
belongs_to :person
belongs_to :company
end
ContactCompany contains a data member called "position". What I want to do is something like:
c = Person.new
c.companies.build(:name => Faker::Company.name, :position => positions.sample)
EDIT:
When I try the code above I get "unknown attribute: position".
The c.companies.build line is attempting to build a Company object which does not have the position attribute (the ContactCompany does) hence the error. It looks like you are trying to set attributes on two different models, so you'll have to make sure you are setting the appropriate attribute on the right model:
# you can chain these calls but I separated them for readability
cc = c.contact_companies.build(:position => positions.sample)
cc.build_company(:name => Faker::Company.name)

Rails 3 - model issue, foreign key

I have two models, Like and Photo.
class Like < ActiveRecord::Base
belongs_to :photo, :class_name => "DataLike", :foreign_key => "photo_id"
end
class Photo < ActiveRecord::Base
has_many :likes
end
And now I try to execute this query:
query = Like.select(:photo_id).joins(:photo).count
But I am still getting this error:
uninitialized constant Like::DataLike
Could anyone help me, please, what I am doing wrong?
Thank you so much
You don't seem to have a DataLike model, my best guess is that you want to link to the Photo model:
class Like < ActiveRecord::Base
belongs_to :photo, :foreign_key => "photo_id"
end
class Photo < ActiveRecord::Base
has_many :likes
end
If you leave out the :class_name option, the Photo model is inferred. It's used to specify the class of the linked model, in case it is different from the association name.

How do i create an object if it has more than one belongs_to?

I have the following:
class Org < ActiveRecord::Base
has_many :users
has_many :entries
end
class Entry < ActiveRecord::Base
belongs_to :org
belongs_to :user
validates_presence_of :entry_text
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end
I can Create Orgs and Users... How do i create an entry if there are two belongs_to? and what is this pattern called?
Double nested resources are tricky. The trick with users usually is to keep it out of your desired entry path.
Your question is kind of broad, but if you specify more information, people would be able to help you better. Also, I would recommend using the gem Devise for your user management system. Since you're using 'users' I would assume you want users from orgs to create entries. The entry created would be a part of org and the user would be the session's current user. Sorry if I am wrong to assume this.
Your routes.rb file can look something like this (assuming rails 3):
resources :orgs do
resources :entries
end
Then the create of your entry controller would look like:
#entry = #org.entries.new(params[:topic])
#entry.user = current_user #or however you are managing the current user's session.
And you'd want to set the org for the entire class by making a method that loads your current org and do a before_filter :loadOrg
def loadOrg
#org = Org.find(params[:id])
end
This is of course assuming your path is something like: /org/(id)/entry/(entry_id)
and not
/org/(id)/user/(user_id)/entry/(entry_id)
which in my opinion is unnecessary and can lead to more problems. You can always create a userpage model that calls all entries by users, but the default route doesn't necessarily have to include users in the path.
I don't see any problem.
#entry = Entry.create(:entry_text => "Hello World!")
Now questions to clarify what do you need:
Can #entry belongs both org and user at the same time? Or it can belongs to only one of them?
Should #entry belongs to at least one of them?
If #entry supposed to belong only one of them, so you should use Polymorphism
http://railscasts.com/episodes/154-polymorphic-association
class Entry < ActiveRecord::Base
belongs_to :textable, :polymorphic => true
validates_presence_of :entry_text
end
class Org < ActiveRecord::Base
has_many :users
has_many :entries, :as => :textable
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries, :as => :textable
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end