Using relations for models with namespace in Rails - ruby-on-rails-3

How do I write relations for models with namespace?
if I have Class Foo::Bar and Class Employee and I want to have habtm between them would I write
Class Foo::Bar
has_and_belongs_to_many :employees
end
and in
Class Employee
has_and_belongs_to_many ???? # <- how do I write this part?
end

You could do something like:
class Employee
has_and_belongs_to_many :foo_bars, :class_name => "Foo::Bar"
end
And then you should be able to access all the Foo::Bar objects on an Employee instance with employee.foo_bars

Related

Query for where method through three associations

I have three models:
Invitation.rb
belongs_to :department
Department.rb
belongs_to :organisation
has_many :invitations
Organisation.rb
has_many :departments
Organization table has tag field.
I need to write a query to search for Invitations, for the organization of which there is a tag field with the content 'First tag'
Something like this:
Invitation.joins(:department).joins(:organisation).where(tag: 'First tag')
But I don't know how to build such a query.
Invitation.joins(department: :organisation).where(organisations: { tag: 'First tag' })
Why? Because to "reach" the organisations table from invitations you can use the relationship between the departments table.
Table.joins(...).joins(...)
Is "joining" twice the same table, what you need is to reference the relatinoship between departments and organisations. That you can do it as:
{ department: :organisation }
Passing that to join loads a JOIN clause in the receiver with departments and in departments with organisations.
Notice the where clause can be used as { organisations: { tag: ... } } or ["organisations.tag = ?", ...], they can be used interchangeably. What you use is just a matter of preference yours or from your colleagues.
To do what you are trying to do without changing your models you should pass a hash to the joins method like so:
Invitation.joins(department: :organisation).where(organisations: { tag: 'First tag' })
This tells ActiveRecord to use the department association as defined on the Invitation model and the organisation association from the Department model
To make your life a bit easier you can add a has_many through association like this:
class Invitation < ApplicationRecord
belongs_to :department
has_one :organisation, through: :department
end
class Department < ApplicationRecord
belongs_to :organisation
has_many :invitations, inverse_of: :department
end
class Organisation
has_many :departments, inverse_of: :organisation
has_many :invitations, through: :departments
end
Now you can use the invitations relation on Organisation as follows:
Invitation.joins(:organisation).where(tag: 'First tag')
To see the sql (e.g. in the console) you can use the #to_sql method:
Invitation.joins(:organisation).where(tag: 'First tag').to_sql
Finally, if you use a scope (and dont like the hash in the where method in which you have to specify the table) you can use a "scope" and merge as follows:
class Organisation
class << self
def just_tagged
where(tag: 'First tag')
end
end
end
Now you can refer to the scope within your query using merge:
Invitation.joins(:organistion).merge(Organisation.just_tagged)

Nested association query

Class User
has_many :universities
end
Class University
belongs_to :user
has_many :courses
end
Class Course
belongs_to :university
end
Now, I want to find the courses of any user.
I can use the following query:
User.find(1).universities.collect{|x| x.courses}
But is there any other simple ways to get this result? Please explain your answer so that I can understand.
Thanks in advance!
Add into User model:
has_many :courses, through: :universities
Now you can fetch all courses of a user via:
User.find(1).courses
From docs (http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association):
A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model.

How to get this SQL query into rails (3) syntax

I have a sql-Statement and I'd like to "convert" it into rails (activerecord) method calls.
This is my query
'SELECT * FROM clients WHERE company_id IN (SELECT company_id FROM companies_projects WHERE project_id= ? )
companies_projects is a join table for an n:n relation of companies and projects
clients belong to companies (1:n)
project is an external resource and has no has_many companies, so I can't go from that direction
I want to get all clients that belong to companies that belong to one project, so I can list them in the index-page
My models
class Client < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :companies_projects
has_many :clients
has_many :projects, :through => :companies_projects
end
I checked the statement in rails console and it works.
I have two problems impelementing this query.
1. find_by_sql
I tried this method
Client.find_by_sql('SELECT * FROM clients WHERE company_id IN (SELECT company_id FROM companies_projects WHERE project_id= ? )',project.id)
But it throws an InvalidStatement Exception, MySQL Syntax Error near "?"
I also tried to put the sql and bindings into an array [sql,bind1], that works but I get an array and need an ActiveRecordRelation
2. where
I'm new to rails and can't figure out a valid method chain for such a query.
Could someone point me in the right direction?
I would prefer using ActiveRecord methods for the query, but I just don't know which methods to use for the nested selects.
You should have following associations between your models:
class Client < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
has_and_belongs_to_many :projects
has_many :clients
end
class Project < ActiveRecord::Base
has_and_belongs_to_many :companies
has_many :clients, through: :companies
end
Then it is simply:
project.clients
Client.where(company_id: CompanyProject.where(project_id: project.id).pluck(:id))
Or you can use JOIN
Client.joins(:company_project).where('companies_projects.project_id = ?', project.id)
But the best solution was proposed by #arup-rakshit
Considering that you have an intermediate model CompanyProject, this can be achieved with following query:
Client.where(:company_id => CompanyProject.where(:project_id => project_id).map(&:company_id) )
[Edit: made company_id a symbol)

Multiple Table Class Inheritence for ruby on rails

I need to set up a multiple class inheritance model here for the following models. Basically I'm building an extensible contacts directory. From a base class Contact I intend to derive other classes i.e something on the lines of :
class Contact
# the super class
attr_accessible :name, :about
end
class Person < Contact
attr_accessible :first_name, last_name, :description, :works_for_company_id
end
class Company < Contact
attr_accessible :company_name, :location, :services
end
Each model corresponds to a different table - I'm assuming that there would be a has_one belongs_to relation ship between teh parent and the child classes however I was wondering if theres a gem that can ease it a bit. Or if I have to do it myself how would I actually accomplish it.
For example contact.name is actually person.first_name+' '+person.last_name for a person or company.company_name for the company. How do I structure my database and associations to get this right?

Rails: has_many :through without additional table

I have a model named Container. This model just has a list of associated Links. So I created tables containers, links and containers_links. Now I want to connect my two models. So I did
class Container < ActiveRecord::Base
has_many :links, :through => :containers_links
end
class Links < ActiveRecord::Base
has_many :containers, :through => :containers_links
end
But I have an error because I don't have reflection with containers_links in my model. I can add something like
has_many :containers_links
But I don't have a model ContainersLinks (and I don't want to create one). What should I do?
You can use has_and_belongs_to_many relationship
class Container < ActiveRecord::Base
has_and_belongs_to_many :links
end
class Links < ActiveRecord::Base
has_and_belongs_to_many :containers
end
But you still need to create join table.