Insert violates foreign key constraints - ruby

My models:
class Team < ApplicationRecord
belongs_to :objective
belongs_to :consultancy
has_one :seminar, :through => :consultancy
belongs_to :consultant, class_name: "Student"
has_and_belongs_to_many :users
end
class User < ApplicationRecord
has_and_belongs_to_many :teams
end
My function:
def find_placement(student)
team = #consultancy.teams.detect{|x| x.has_room && student.objective_students.find_by(:objective => x.objective).obj_ready_and_willing?}
team.users << student if team && team.valid? && team.persisted?
return team
end
And this is the error I'm currently receiving:
PG::ForeignKeyViolation: ERROR: insert or update on table "teams_users" violates foreign key constraint "fk_rails_8299e376e9" DETAIL: Key (team_id)=(3396) is not present in table "teams". : INSERT INTO "teams_users" ("user_id", "team_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4)
This error only occurs in production environment, not development or testing.
I've tried using console mode to manually run each line of the function. No error is triggered.
From this question https://dba.stackexchange.com/questions/29147/postgresql-insert-update-violates-foreign-key-constraints, I found this explanation.
"If a new row is inserted, check DataID and Address: if they contain a non-NULL value (say 27856), then check Table1 for DataIDĖ™and Table2 for Address. If there is no such value in those tables, then return an error."
If I understand this paragraph correctly, my function is trying to insert a student into a team that doesn't exist. But doesn't the "if team && team.valid? && team.persisted?" part of my code take care of that? If not, what is a better way to insert the student into a team, but only if the team already exists.
Thank you in advance for any insight.

The valid? method does not check database persistence, so you may or maynot need it. You'll probably want to insure the team model is persisted and not deleted from the teams table. To be safe, you might check both. ActiveRecord#persisted? method:
def find_placement(student)
if team = #consultancy.teams.detect{|x| x.has_room && student.objective_students.find_by(:objective => x.objective).obj_ready_and_willing?}
team.users << student if team.valid? && team.persisted?
end
team
end

The key to getting past this was to reload.
Before, #consultancy was collecting all of the Teams, including some that had been destroyed.
#consultancy.reload.teams
only collects the teams that still exist.
I would not have come to this conclusion without the comment from #SergioTulentsev and the answer from #lacostenycoder. Thanks!

Related

Rails 5: Insert/Update only if there's no filled specific field for that id

I have a Followups table with the fields: patient_id, death_date (and other fields..).
There could be multiple records for the same patient_id but there must be only one death_date for that patient_id.
A unique index won't work as the user could insert two different death_date.
Which is the best way to achieve this in Rails 5?
If possible, please make an example.
Thanks
You could do this with a callback on the Followup model:
Assuming a Patient has_many :followups
class Followup
belongs_to :patient
validate :check_for_existing_death_date
private
def check_for_existing_death_date
# This will grab the first one if any with a death date exist
# This also assumes that the patient_id and patient exist
followup = patient.followups.where("death_date IS NOT NULL").take
# if you want to raise an error...
if followup
errors.add(:death_date, 'There is already a death date for this patient')
return false
end
# If not, do something with the data
# if followup
# self.death_date = nil # or followup.death_date if you want it saved on followups as well
# end
end
end
I would think that the best way to do this would be to store the death_date on the Patient record, since death only happens once per patient.

Rails: find_by making additional sql query, previously did includes

I've got three classes:
class Batch
has_many :final_grade_batches
end
class FinalGradeBatch
belongs_to :batch
belongs_to :student
end
class Student
has_many :final_grade_batches
end
I want to retrieve a final_grade_batch the following way:
batch = Batch.includes(:final_grade_batches).find(1)
batch.final_grade_batches.find_by(student_id: 2)
The final lines produces this SQL query:
FinalGradeBatch Load (0.6ms) SELECT "final_grade_batches".* FROM "final_grade_batches" WHERE "final_grade_batches"."batch_id" = $1 AND "final_grade_batches"."student_id" = 2 LIMIT 1 [["batch_id", 1]]
If I included final_grade_batches in the Batch find query, why is it looking for the final grade batch again? I know it needs to find the one that has the student's id, but should Rails make a query to get that? Isn't it loaded into memory by now?
Is there any way I can get a final grade batch without Rails hitting the database again? Thanks!
find_by always makes database queries. Substitute it with:
batch.final_grade_batches.select( |grade_batch| grade_batch.student_id == 2).first
This works with the array you already loaded from the database instead of doing an additional query.

Using ActiveRecord with column names with spaces in them

I work on a team that is using ActiveRecord to access the schema on a MSSQL server. Modifying the schema is not an option and the column names have spaces in them, a la SPACEY COLUMN.
When writing the ActiveRecord class to access a table with spaces in it, what is good practice?
We also need this to work with factory girl...
AR-JDBC (as well as the AR-SQLServer-Adapter) will/should handle this just fine since it auto-magically quotes column name identifiers using "[ COlumn NAME ]" ... I personally would hide this from bubbling up as much as possible e.g. using aliases :
class MySpacey < ActiveRecord::Base
set_table_name 'SPACEY TABLE'
set_primary_key 'MY ID'
alias_attribute :id, :'MY ID'
end
Consider User is the Model and User Name is the column you need to access.
User.where('User Name' => 'Bob')
You can add multiple conditions also,
User.where('User Name' => 'Bob', 'Email Address' => 'sample#sample.com')
You can also try,
User.where('[User name] = ? AND [Email Address] = ?', 'Bob', 'sample#sample.com')
And if your table itself has space in it. Try,
class User < ActiveRecord::Base
self.table_name = "user table"
end

How to disable sequence in activerecord-oracle_enhanced-adapter without rails?

I am only using ActiveRecord to some development with a legacy Oracle database. My adapter is activerecord-oracle_enhanced-adapter (https://github.com/rsim/oracle-enhanced). So I don't want to AR handle the primary key generation. How to disable the primary key generated by a sequence?
class User < Activied::Base
self.table_name = "users"
self.primary_key = "user_id"
end
user = User.new
user.save
Then I got the error:
stmt.c:230:in oci8lib_191.so: ORA-02289: sequence does not exist (OCIError)
When I change my code to
class User < ActiveRecord::Base
self.table_name = 'users'
self.primary_key = "user_id"
self.sequence_name = nil
end
I got another error:
stmt.c:230:in oci8lib_191.so: ORA-00936: missing expression (OCIError)
So is there anyone know how to manage the primary key manually? I just want to do some simple insert.
Thanks
Maybe it's too late but finaly I found answer. Shortly:
self.sequence_name = :autogenerated
From the source code comment here:
Usage notes:
# * Key generation assumes a "${table_name}_seq" sequence is available
# for all tables; the sequence name can be changed using
# ActiveRecord::Base.set_sequence_name. When using Migrations, these
# sequences are created automatically.
# ***Use set_sequence_name :autogenerated **** with legacy tables that have
# triggers that populate primary keys automatically.**

ROR- Cannot use Find in a 1-many relationship

In Ruby on rails, our model includes orders and payments.
There's 1-many relationship between order and payments.
In the orders model we specify:
has_many :payments, :as => :payable
And a payment record has payable_id that is set to order.id.
In a report, I want to select all payments that belong to orders of a given type.
Using:
payments = Payment.find(:all, :conditions => conditions)
and adding 'payable.type="a" ' to the conditions doesn't work.
It seems that ActiveRecord doesn't develop this into a correct join statement (payable_id=order.id and orders.type='a').
I cannot use explicit SQL here, as the condition contains other things that are inserted there earlier in the code.
Thanks,
Raffi Lipkin
Your conditions clause is wrong.
You state that an Order
has_many :payments, :as => :payable
This tells me that a Payment
belongs_to :payable, :polymorphic => true
This means that the payments table has two columns of note: payable_id and payable_type. This also means that Payments can be applied not just to Orders, but also to other models as well (CreditCardBalances, who knows).
If you want to query for payments of a specific type, i.e. belonging to any instance of a particular class, you need to be querying the field payments.payable_type. This works fine:
Payment.find(:all, :conditions => "payable_type = 'Order'")
Here's a gist that shows what I did to test this. The models created are set up just like described above.
Don't forget that you can extract that into named scopes if it's easier:
named_scope :on_orders, :conditions => "payable_type = 'Order'"
Which makes it
Payment.on_orders
Or dynamically:
named_scope :on, lambda { |type| { :conditions => "payable_type = '#{type.to_s}'" } }
Which then makes it
Payment.on(Order) # or Payment.on(CreditCardBalance) or Payment.on("Order")
Try incliding and reference the actual table id name in the condition, rather than the association alias:
find(:include => "payments", :conditions => ["payment.type = ?", "x"]
You mention 'payment type'. If they're fairly static, have you considered using single table inheritance (STI) to subclass your different payment types? Then Rails will do all the magic to filter on type.
E.g.
class CreditCardPayment < Payment
...
end
It doesn't even need to exhibit different behaviour initially; however you'll probably find that it turns out to be really useful to have different data and polymorphic behaviour around payments.

Resources