How to use finder methods on join table values? - ruby-on-rails

In my Ruby on Rails app, i've created a join table in my database to link customers and businesses. Now i'm trying to find information on one of the models based on the information in the join table (called "conflicts"):
#customer = Customer.where(customer_id: 1).conflict(s) Or
#customer = Customer.find(1).conflict(s)
I want to find information about the customer (or business) directly tied to a given "conflict", and print that to the browser view.
Error i'm getting now:
undefined method `conflicts' for #

To get conflict through customer you should have following association in Customer model(customer.rb),
has_one :conflict
OR
has_many :conflicts

As per the description provided you have created a join table having the reference of both customers and businesses.
And the error you have provided, it seems like you forgot to associate them in your models.
customer.rb
class Customer < ApplicationRecord
has_many :customer_conflicts
has_many: conflicts, through: :customer_conflicts
end
conflict.rb
class Conflict < ApplicationRecord
has_many :customer_conflicts
has_many :customers, through: :customer_conflicts
end
customer_conflict.rb
class CustomerConflict < ApplicationRecord
belongs_to :customer
belongs_to :conflict
end
Now running the below mentioned queries will work:
#customer = Customer.where(customer_id: 1).conflicts
#customer = Customer.find(1).conflicts

Related

Active Record includes with a belongs_to and has_many doesn't return associations

I have the following models:
user.rb
class User < ApplicationRecord
has_many :invitations
end
organization.rb
class Organization < ApplicationRecord
has_many :invitations
end
invitation.rb
class Invitation < ApplicationRecord
belongs_to :user
belongs_to :organization
end
I'm trying to query Active Record in the following way:
user = User.find(params[:id])
user.invitations.includes(:organization)
I want to be able to get all invitations for the user and also have the invitations include attributes of their related organization. However, I am only getting the invitation and none of the organization's attributes.
Even if I try:
Invitation.includes(:organization)
I'm still not getting each invitation's associated organization.
Any and all help is greatly appreciated!
includes method provides eager loading. This solves the N + 1 queries problem. You can access the loaded organization like user.invitations.first.organization. There will be no new queries here.
If you want to combine invitation and organization attributes, you can use joins and select.
user.invitations
.joins(:organization)
.select('invitations.*, organizations.foo, organizations.boo as blabla')
#demir has already answered your question.
Just as an addition - probably, you mixed up includes for models with include option of to_json \ as_json.
If you want to return some JSON result (e.g. in your API response), then you can do user.as_json(include: [invitations: { include: :organization }])

rails build associations from has_many through has_many raise error

I have a very special cases. I understand maybe db design is not very awesome, but I cannot change that.
class Employer < ApplicationRecord
has_many :contract_employers
has_many :contracts, through: :contract_employers
has_many :crm_contacts, through: :contract_employers
# typical join table, with key: contract_id and employer_id
class ContractEmployer < ApplicationRecord
belongs_to :contract
belongs_to :employer
has_many :crm_contacts
# CrmContact table has key: contract_employer_id
class CrmContact < ApplicationRecord
belongs_to :contract_employer
has_one :employer, through: :contract_employer
Given
employer = Employer.create
I have no issue to run
employer.contracts.create
However, if I try to run
employer.crm_contacts.create
It raise error
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'Employer#crm_contacts' because the source reflection class 'CrmContact' is associated to 'ContractEmployer' via :has_many.
I checked the rails source code, basically it states very clearly
# Construct attributes for :through pointing to owner and associate. This is used by the
# methods which create and delete records on the association.
#
# We only support indirectly modifying through associations which have a belongs_to source.
# This is the "has_many :tags, through: :taggings" situation, where the join model
# typically has a belongs_to on both side. In other words, associations which could also
# be represented as has_and_belongs_to_many associations.
#
# We do not support creating/deleting records on the association where the source has
# some other type, because this opens up a whole can of worms, and in basically any
# situation it is more natural for the user to just create or modify their join records
# directly as required.
So only typical join table supports model.associations.create? Any suggestion for my user case?
Take my case for example, even rail is willing to do the job. How could employer.crm_contacts.create create middle table record ContractEmployer? Yes, it knows employer.id, but it has no clue what contract.id is, right?
Rails can not create middle table record in this case, but you can.
And I am completely agree with this (comments in rails source code /activerecord/lib/active_record/associations/through_association.rb):
in basically any situation it is more natural for the user to just
create or modify their join records directly as required
I don't see a problem here.
class Employer < ApplicationRecord
# ...
def create_crm_contact
ActiveRecord::Base.transaction do
contract = contracts.create # will create both `contract` and associated `contract_employer`
# find the `contract_employer` that has been just created
contract_employer = contract_employers.find_by(contract_id: contract.id)
contract_employer.crm_contacts.create
end
end

Create Rails scope comparing fields on two tables

I have a number of associated tables in an application
class Listing < ActiveRecord::Base
belongs_to :house
belongs_to :multiple_listing_service
end
class House < ActiveRecord::Base
has_one :zip_code
has_one :primary_mls, through: :zip_code
end
I wanted to create a scope that produces all the Listings that are related to the Primary MLS for the associated House. Put another way, the scope should produce all the Listings where the multiple_listing_service_id = primary_mls.id for the associated house.
I've tried dozens of nested joins scopes, and none seem to work. At best they just return all the Listings, and normally they fail out.
Any ideas?
If I understand correctly, I'm not sure a pure scope would be the way to go. Assuming you have:
class MultipleListingService < ActiveRecord::Base
has_many :listings
has_many :zip_codes
end
I would go for something like:
class House < ActiveRecord::Base
...
def associated_listings
primary_mls.listings
end
end
Update 1
If your goal is to just get the primary listing then I would add an is_primary field to the Listing. This would be the most efficient. The alternative is a 3 table join which can work but is hard to optimize well:
class Listing < ActiveRecord::Base
...
scope :primary, -> { joins(:houses => [:zip_codes])
.where('zip_codes.multiple_listing_service_id = listings.multiple_listing_service_id') }

How to write active record query to details by using and

i have two tables
1)Properties :fields are id, name, propert_type,category_id
2)Admins : fields id, name,mobile,category_id
i want to write an active record to list all properties , where category_id in properties table and category_id in Admins table are equal, according to current_user_id
i am listing this property list by logging as admin.
model relation
class Category < ActiveRecord::Base
has_many :admins,dependent: :destroy
has_many :properties,dependent: :destroy
end
class Admin < ActiveRecord::Base
belongs_to :category
has_many :properties
end
class Property < ActiveRecord::Base
belongs_to :admin
belongs_to :category
end
i wrote active record like this , but i got error,
can anyone please suggest me a solution for this
#properties= Property.where('properties.category_id=?','admins.category_id=?').and('admins.id=?',current_user.specific.id)
With your assosciation,You can use a sub query for getting your result in one line
#properties = Property.where(category_id: Admin.select("category_id").where(id: current_user.id))
As per my understanding current_user is an Admin. So You can search by the category_id of current_user. If I'm right, try this
#properties = Property.where(category_id: current_user.category_id)

has_many & belongs_to is not working in rails

I have created two models 1) Contact 2) Customer in my Rails application, now I want to join the two tables of these models. Tables are contacts & customers respectively. I am using following code:
1) contact.rb:
class Contact < ActiveRecord::Base
unloadable
has_many :customers
end
2) customer.rb
class Customer < ActiveRecord::Base
belongs_to :contact, :foreign_key => :contact_id`
end
3) customers_controller.rb
def new
#customer = Customer.new
#customer = Customer.find(:all,:include => :contact_id)
end
Here I am trying to access the primary key of contact table into customer table but it repeatedly gives this error "Association named 'contact_id' was not found; perhaps you misspelled it?" Can any one help me on this?
When you use include, you should pass in the association name (in your case "contact") rather than the foreign key.
However, your description doesn't make clear that this is what you want to do, so if you can clarify your question I'll update this answer if it's wrong
If I clearly understand you don't need to pluralize customer in the contact model :
class Contact < ActiveRecord::Base
unloadable
has_many :customers
end
And you don't need to specified the name of the column who contain the foreign key
(sorry for my english)

Resources