How to query has_one through relationship? - ruby-on-rails

My models:
metro_station.rb
class MetroStation < ActiveRecord::Base
belongs_to :metro_line
has_one :city, through: :metro_line, autosave: false
end
metro_line.rb`
class MetroLine < ActiveRecord::Base
belongs_to :city
has_many :metro_stations
end
city.rb
class City < ActiveRecord::Base
has_many :metro_lines
end
When I run:
MetroStation.where(city: City.first)
I get
PG::UndefinedColumn: ERROR: column metro_stations.city_id does not exist
: SELECT "metro_stations".* FROM "metro_stations" WHERE "metro_stations"."city_id" = 1
(pry) output error: #<ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column metro_stations.city_id does not exist
LINE 1: ...CT "metro_stations".* FROM "metro_stations" WHERE "metro_sta...
While this query works:
MetroStation.joins(:metro_line).where(metro_lines: {city_id: 1})

To find out why your first approach doesn't work type in
MetroStation.where(city: City.first).to_sql
You should see something like
... WHERE metro_stations.city_id = 1
forming part of the query. The MetroStation model simply doesn't have the city_id attribute, as a result your first approach forms an invalid SQL statement.
The join works since it is filtering on the MetroLine table which has the relationship to the City model in form of the city_id field.
Nothing wrong with your models it is just the way Rails generates the SQL statements which in the world of SQL makes sense.
A has_many relationship on City to MetroStation through MetroLine delivers the desired results for your question (which metro stations are in a city).
Example
class City < ActiveRecord::Base
has_many :metro_lines
has_many :metro_stations, through: :metro_lines
end
class MetroLine < ActiveRecord::Base
belongs_to :city
has_many :metro_stations
end
class MetroStation < ActiveRecord::Base
belongs_to :metro_line
has_one :city, through: :metro_line
end
# Return all metro stations for a city
# * assuming City has name 'name' field
london_stations = City.find_by_name('London').metro_stations
Hope this helps!

City should also:
has_many :metro_stations, :through => :metro_lines
And then you write:
City.first.metro_stations

Related

Matching the records in ruby on rails

I have a model company, which has association, has many candidates, and belongs to company.
And I have another model key_skill, which has association, has many key_skills, and belongs to candidate.
Another model is candidate, which belongs to company, and has many key skills association.
I am trying to get the candidate whose key skills are matched to the required skill and, it should search and get the candidate who belongs to the particular company.
How can I write a query in the model for this situation?
These are the associations
company.rb
class Company < ActiveRecord::Base
has_many :candidates
end
candidate.rb
class Candidate < ActiveRecord::Base
belongs_to :company
has_many :key_skills, dependent: :destroy
accepts_nested_attributes_for :key_skills, reject_if: :all_blank,
allow_destroy: true
end
key_skill.rb
class KeySkill < ActiveRecord::Base
belongs_to :candidate
end
I think your current association condition is like this:
class Company < ApplicationRecord
has_many :candidates
end
class Candidate < ApplicationRecord
belongs_to :company
has_many :key_skills
end
class KeySkill < ApplicationRecord
belongs_to :candidate
end
For example to fetch all candidates with key_skills with ids 1,2,3 run the following query
Candidate.joins(:company, :key_skills).where("key_skills.id in (?)", [1,2,3])
Try the below:
I am assuming that key_skills table have a field skill and you want to perform search on it.
candidate = Candidate.includes(:company, :key_skills).where("key_skills.skill like ?", "%#{params[:skill]}%")
company = candidate.company

How do I specify the join table on a has_many through association?

Here is the schema information on my tables:
table_name: admin_users, primary_key: id
table_name: UserCompanies, primary_key: UserCompanyId, foreign_keys: [CompanyId, UserId]
table_name: Companies, primary_key: CompanyId'
I want to do something like the following:
AdminUser.first.companies
But, my attempts so far are not working, I'm assuming because I need to specify the table names, model names, or key names, but I don't know how that works with a has_many through relationship. Here is my best attempt at defining it so far:
class AdminUser < ActiveRecord::Base
has_many :user_companies, class_name:"TableModule::UserCompany", foreign_key:"UserId"
has_many :companies, through: :user_companies, class_name: "TableModule::Company"
end
# this code is from a rails engine separate from the app where AdminUser is defined
# the purpose of the engine is to provide access to this particular database
# the CustomDBConventions class adapts the models for this database to work with ActiveRecord so we can use snake case attributes, reference the primary key as 'id', and it specifies the correct tables names.
module TableModule
class UserCompany < CustomDBConventions
belongs_to :admin_user
belongs_to :company
end
class Company < CustomDBConventions
has_many :admin_users, through: :user_companies
end
class CustomDBConventions < ActiveRecord::Base
self.abstract_class = true
def self.inherited(subclass)
super
subclass.establish_connection "table_module_#{Rails.env}".to_sym
tb_name = subclass.table_name.to_s.gsub(/^table_module_/,"").classify.pluralize
subclass.table_name = tb_name
subclass.primary_key = tb_name.singularize + "Id"
subclass.alias_attribute :id, subclass.primary_key.to_sym
subclass.column_names.each do |pascal_name|
subclass.alias_attribute pascal_name.underscore.to_sym, pascal_name.to_sym
subclass.alias_attribute "#{pascal_name.underscore}=".to_sym, "#{pascal_name}=".to_sym
end
end
end
end
EDIT: So this setup is really close and I am missing only 1 foreign key specification. When I run AdminUser.first.companies I get a sql error:
TinyTds::Error: Invalid column name 'company_id'.: EXEC sp_executesql N'SELECT [Companies].* FROM [Companies] INNER JOIN [UserCompanies] ON [Companies].[CompanyId] = [UserCompanies].[company_id] WHERE [UserCompanies].[UserId] = #0', N'#0 int', #0 = 1
So I just need to specify to use UserCompanies.CompanyId on this join. How do I properly specify this foreign key?
Assuming the TableModule::UserCompany model has these associations...
class TableModule::UserCompany < ActiveRecord::Base
belongs_to :admin_user
belongs_to :company
end
...then I think this is what you're after:
class AdminUser < ActiveRecord::Base
has_many :companies, through: :user_company, class_name: "TableModule::UserCompany"
end
I'm uncertain what you're doing with the TableModule prefixes, but the following should work:
class AdminUser < ActiveRecord::Base
has_many :user_companies
has_many :companies, through: :user_companies
end
class Company < ActiveRecord::Base
has_many :user_companies
has_many :admin_users, through: :user_companies
end
class UserCompany < ActiveRecord::Base
belongs_to :admin_user
belongs_to :comany
end

How can I multiple insert the data in rails

I want to insert multiple data in rails. I'm using postgresql, the scenario is when the form submit it passes client name, email and some personal info, then also pass the desire venue with the date and also the amenities they want (ex. swimming poll, billiard poll and etc.). In my backend I will query :
venue = Venue.find(theVenue_id)
book = venue.books.new(name: client_name, email: client_email and etc)
My question is how can I insert the data in my amenity_books if the had many amenities choosen?
I trie something like this.
ex. amenities_id_choosen = [1,3]
if book.save
amenities_id_choosen.each do |x|
amenity = Amenitiy.find(x)
amenity_book = amenity.amenity_books.create(venue_id: venue.id)
end
I know this is not a good idea to insert data but that was my last choice. Does any body knows how to insert multiple data in 2 model with different data.
Models
class Amenity < ActiveRecord::Base
has_many :categorizations
has_many :venues, through: :categorizations
has_many :amenity_books
has_many :books, through: :amenity_books
end
class Venue < ActiveRecord::Base
has_many :categorizations
has_many :amenities, through: :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :venue
belongs_to :amenity
end
class Book < ActiveRecord::Base
belongs_to :venue
end
class AmenityBook < ActiveRecord::Base
belongs_to :amenity
belongs_to :venue
belongs_to :book
end
Here's an improved version:
amenities_id_choosen = [1,3]
if book.save
Amenitiy.find(amenities_id_choosen).each do |amenity|
amenity.amenity_books.create(venue_id: venue.id)
end
end
This will result in one SELECT query to find all chosen amenities and one INSERT query for each selected amenity.
Another option is to change your data model, does AmenityBook really need to have a venue? Because it looks like the venue is defined through the Book model already.
Here's a suggestion:
class Book < ActiveRecord::Base
belongs_to :venue
has_many :amenity_books
has_many :amenities, through: :amenity_books
end
class AmenityBook < ActiveRecord::Base
belongs_to :amenity
belongs_to :book
has_one :venue, through: :book
end
The code to create a booking with many amenities:
amenities_id_choosen = [1,3]
book.amenity_ids = amenities_id_choosen
if book.save
# success !
end

Problems with CamelCase and underscore Rails 3

I have two models with a has many through association between them like below:
TipoDocumento < ActiveRecord::Base
has_many :dependencias
has_many :TipoRequisitos, :through => :dependencias
...
end
TipoRequisito < ActiveRecord::Base
has_many :dependencias
has_many :TipoDocumentos, :through => :dependencias
...
end
Dependencia < ActiveRecord::Base
belongs_to: TipoDocumento
belongs_to: TipoRequisito
...
end
The id's attributes for the join model Dependencia are TipoDocumento_id and TipoRequisito_id.
Now, when I try this in the rails console:
x = TipoDocumento.find(1)
x.TipoRequisitos
I get this error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: dependencia.tipo_documento_id: SELECT "tipo_requisitos".* FROM "tipo_requisitos" INNER JOIN "dependencia" ON "tipo_requisitos"."id" = "dependencia"."TipoRequisito_id" WHERE "dependencia"."tipo_documento_id" = 1
also if I try the opposite with TipoRequisito it's the same.
It seems that Rails is changing somehow the TipoDocumento_id column name for tipo_documento_id when it performs the query. So, I tried to change the id's column's names from their CamelCase to their snake_case, but I get the analog error (Cannot find TipoDocumento_id or TipoRequisito_id.)
I don't see what's wrong.
You need to follow the Rails convention and use down-cased names when you refer to models when defining relations:
TipoDocumento < ActiveRecord::Base
has_many :dependencias
has_many :tipo_requisitos, through: :dependencias
...
end
TipoRequisito < ActiveRecord::Base
has_many :dependencias
has_many :tipo_documentos, :through => :dependencias
...
end
Dependencia < ActiveRecord::Base
belongs_to :tipo_documento
belongs_to :tipo_requisito
...
end
you need to lower-case it, like this:
x = TipoDocumento.find(1)
x.tipo_requisitos
Please also check: http://guides.rubyonrails.org/association_basics.html

Rails Inheritance with relationships problem

I am using single table inheritance in my project. Instead of explaining more, I'll give the code:
# person_profile.rb
class PersonProfile < ActiveRecord::Base
belongs_to :Person
end
# company_profile.rb
class CompanyProfile < ActiveRecord::Base
belongs_to :Company
end
# person.rb
class Person < User
has_one :PersonProfile
end
# company.rb
class Company < User
has_one :CompanyProfile
end
This seems to me like it should work fine. In one of my views I try if #person.PersonProfile == nil which makes perfect sense to me. But Rails doesn't like it:
Mysql::Error: Unknown column 'person_profiles.person_id' in 'where clause': SELECT * FROM `person_profiles` WHERE (`person_profiles`.person_id = 41) LIMIT 1
Rails is looking for person_id in the table person_profiles, but there is only a user_id in that table. What is the best way to fix this bug?
You can use the :foreign_key option of has_one to specify the key.
For example:
has_one :person, :foreign_key => "user_id"
See this reference.
The models specified in your model associations should be in lowercase with each word being separated by an underscore. So:
class PersonProfile < ActiveRecord::Base
belongs_to :person
end
class CompanyProfile < ActiveRecord::Base
belongs_to :company
end
class Person < User
has_one :person_profile
end
class Company < User
has_one :company_profile
end
i had to specify the foreign_key as 'user_id' because it thinks its 'person_id' by default.
class Person < User
has_one :person_profile, :foreign_key => 'user_id'
end
class Company < User
has_one :company_profile, :foreign_key => 'user_id'
end

Resources