Why activerecord is mapping unassigned foreign key? - ruby-on-rails-3

I am mapping my foreign key by declaring it in the class.
But rails is calling auto assigned foreign key and throwing error as,
unknown column in where clause
Class declaration is given below,
class Supplier < ActiveRecord::Base
has_one :criteria
self.primary_key = 'sup_id'
end
class Criteria < ActiveRecord::Base
belongs_to :supplier, :foreign_key => "crt_sup_id"
self.primary_key = 'crt_id'
self.table_name = 'criterias'
end
If I am using self.supplier in class Criteria it is working fine
but If I am using self.criteria in class Supplier then error thrown as,
Unknown column 'criterias.supplier_id' in 'where clause': SELECT criterias.* FROM criterias WHERE criterias.supplier_id
I don't have a field named supplier_id in criterias instead I am using crt_sup_id as defined in the class.

You have to reference the foreign key in the has_one relation as well :
class Supplier < ActiveRecord::Base
has_one :criteria, foreign_key: "crt_sup_id"
self.primary_key = 'sup_id'
end
The reason is that by default associations are one-way in rails. See the rails guides for more information.

Related

Creating a "joins" query 2 associations up

I have a situation where a CourseSession belongs to a Course, which belongs to a Program. In my controller I'd like to get the name of the program from the CourseSession. I've been looking around and have been seeing a lot of people suggest using the joins method. Unfortunately for me this didn't work in my case. I get this error:
Can't join 'CourseSession' to association named 'program'; perhaps you misspelled it?
What am I doing wrong?
#sessions = if params[:program]
CourseSession.joins(:course).joins(:program).where("program.name = params[:program]")
else
CourseSession.all
end
class Program < ApplicationRecord
has_many :courses, dependent: :nullify
end
class Course < ApplicationRecord
has_many :sessions, class_name: "CourseSession", inverse_of: :course, dependent: :destroy
belongs_to :program
end
class CourseSession < ApplicationRecord
belongs_to :course
end
When you use CourseSession.joins(:course).joins(:program) (i.e. chaining joins), you're just joining course_sessions table with courses table and programs table hence the error.
What you've asked for can be achieved using the following syntax:
CourseSession.joins(course: :program)
Here, courses and programs table are inner joined and course_sessions and courses table are inner joined.
Second issue is in your where method. The table names are plural by convention so you should be using programs.name instead of program.name. Try the following instead:
CourseSession.joins(course: :program).where("programs.name = ?", params[:program])
It's a little unclear what your trying to do. Your code suggests you want:
CourseSessions associated with the Program if params[:program] is present, and
All CourseSessions if params[:program] is not present.
In which case I believe you'd do something like:
#sessions = if params[:program]
CourseSession.where(course: Course.where(program: Program.find_by(name: params[:program])))
else
CourseSession.all
end

Changing from joins to includes breaking query

I had this working query:
#search = Availability.joins{facility.activities}
.where{activities.id == s_activity}
But my view was getting a lot of information from Facilities and this resulted in an N+1 issue.
So I decided I should be using includes instead to eager load my associations
#search = Availability.includes{facility.activities}
.where{ facility.activities.id == s_activity)}
But this results in an error:
!! #<ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "activities"
LINE 1: ...T "availabilities".* FROM "availabilities" WHERE ("activitie...
^
These are the associations:
class Activity < ActiveRecord::Base
has_and_belongs_to_many :facilities
end
class Availability < ActiveRecord::Base
# Associations
belongs_to :facility
end
class Facility < ActiveRecord::Base
# Associations
has_and_belongs_to_many :activities
has_many :availabilities
end
There's a table called activities_facilities for the has_and_belongs_to_many
You need to append .references when using includes with conditions.
Availability.includes(facility: [:activities]).where('activities.id = ?', s_activity).references(:facility, :activities)
If you want to add conditions to your included models you’ll have to
explicitly reference them
Refer conditions part in includes

Check if record exist, if yes update else create new - nested forms - rails

I'm using rails 3.2. I have a nested form for "Samples". A "Patient" can have several "Samples" but a "Sample" can only have one "Patient".
Patient primary key is "id" but is has also 4 foreign keys: nid, province_id, district_id and facility_id.
Here's the models (i'm trying to show only the relevant code):
Patient.rb
class Patient < ActiveRecord::Base
attr_accessible :date_of_birth, :infant_name, :nid, :province_id, :district_id, :facility_id, :gender_id, :gender_atributes
belongs_to :gender
belongs_to :province
belongs_to :district
belongs_to :facility
has_many :samples
accepts_nested_attributes_for :gender
validates :province_id, presence: true
validates :district_id, presence: true
validates :facility_id, presence: true
validates :nid, presence: true
#To validate uniqueness of patient, that is, one with unique nid, province, district and facility
validates_uniqueness_of :nid, :scope => [:province_id, :district_id, :facility_id]
end
Sample.rb
class Sample < ActiveRecord::Base
attr_accessible :dateOfSample, :nrOrdem, :patient_id,:facility_id, :province_id, :district_id
belongs_to :facility
belongs_to :patient
belongs_to :province
belongs_to :district
accepts_nested_attributes_for :patient
end
What I want is to avoid corrupted data on pacient, that is, to have different patients with the same (nid, province_id, district_id and facility_id).
Right now the nested form for sample lets me create a new patient and if I try to create a new one with the same (nid, province_id, district_id and facility_id) I get an error saying that the "nid is already taken".
But, as a Patient can have different samples, I want to (while trying to create the record) check if the patient already exists, if it does than adds "patient_id" to the "sample" record, otherwise creates a new one.
It would be great to also show a message saying that the patient already exists and fill the corresponding data on the form, but for now I'll be glad if it adds the patient_id to the sample record, ignoring the rest of the patient fields in the nested form.
I tried to implement something like this
rails: create Parent, if doesn't exist, whilte creating child record but it didn't work.
How can I solve this?
EDIT
Here's how the code ended up after #James Mason help.
On Sample.rb
def patient_attributes=(attrs)
self.patient = Patient.where({:nid => attrs[:nid], province_id: province_id, district_id: district_id, facility_id: facility_id}).first_or_initialize(attrs)
end
You're looking for first_or_create and/or first_or_initialize. In this case, since you care whether the record already existed, you probably want the initialize version.
#patient = Patient.where({nid: nid, province_id: province_id, district_id: district_id, facility_id: facility_id}).first_or_initialize
#show_exists_message = !#patient.new_record?
#patient.save
You can either put this code in your controller's create action, or, since you're using nested attributes, define a custom Sample#patient_attributes= method:
class Sample < ActiveRecord::Base
belongs_to :patient
accepts_nested_attributes_for :patient
def patient_attributes=(attrs)
self.patient = Patient.where(attrs).first_or_initialize(attrs)
#show_exists_message = !#patient.new_record?
end
end

Approaching custom foreign keys

I have the following tables
User.
Name
Email
..
password_digest
Company.
Name
Address
...
contact_person_id
So my company have a contact_person, which take a user_id, therefor being a foreign key.
I have tried the following.
class Company < ActiveRecord::Base
has_one(:user, foreign_key: 'contact_person_id')
end
class User < ActiveRecord::Base
belongs_to(:company, :class_name => "Company", :foreign_key => 'contact_person_id')
end
But when i try to run my (respond_to) rspecs i get
Failure/Error: before { #company = FactoryGirl.create(:company) }
ActiveModel::MissingAttributeError: can't write unknown attribute `contact_person_id'
My Factory
FactoryGirl.define do
factory :company do
name "Starup Company"
address "Test street 37"
zip 2200
website "http://example.com"
industry "Construction"
contact_person user
end
end
What am i doing wrong? And how would i point to user with a company object?
company.contact_person
2 issues - you have belongs_to and has_one reversed and you are calling contact_person method on a company, but you defined the relationship to be as a user
if you want to use the contact_person method on the company, try this:
class Company < ActiveRecord::Base
belongs_to(:contact_person, class_name: 'User')
end
class User < ActiveRecord::Base
has_one(:company, :foreign_key => 'contact_person_id')
end
This page has a good overview: http://guides.rubyonrails.org/association_basics.html#choosing-between-belongs-to-and-has-one The foreign key ALWAYS is found in the table with the belongs_to association.
The way you wrote it each user can only have one company that they are the contact person for.
To write the rails code the way you have it, you'd have to have the foreign key in the users table.

NoMethodError when using :include on association in Rails 3 with Active Record

Obviously I'm missing something simple here. Here are my two classes and the code I'm calling. When I use :include with find, it blows up and gives me a NoMethodError on the find line. When I don't use :include it works just fine (but obviously doesn't do the join)
Called Code
def index
#users = User.find(:all, :include => [:org])
end
Classes
class User < ActiveRecord::Base
belongs_to :org, :primary_key => :org_id
end
class Org < ActiveRecord::Base
#define primary key because it is not :id
#because this table is in an old db
#that can't be changed
set_primary_key :org_id
has_one :user
def full_name
"#{emp_fname} #{emp_lname}"
end
end
The exact error
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each):
What version of rails are you on? Your tag says 3 - is it 3.1 or one of the 3.0.x series? Regardless, it seems in 3.0.x this :include hash syntax for find isn't supported.
Try User.includes(:org).find(:all).
Ok, so after a lot of digging and trial and error, basically it was a combination of having one table in one schema and the other in a different schema. By specifying the fully qualified table name for our active record objects, active record stopped throwing up on itself.
So the final code:
Called Code
def index
#users = User.includes(:org)
end
Classes
class User < ActiveRecord::Base
set_table_name "DOC_REQUEST.USERS"
belongs_to :org, :primary_key => :org_id
end
class Org < ActiveRecord::Base
set_table_name "AOS.ORG"
#define primary key because it is not :id
#because this table is in an old db
#that can't be changed
set_primary_key :org_id
has_one :user
def full_name
"#{emp_fname} #{emp_lname}"
end
end