Rails: Build Association when id Method is Non-standard - ruby-on-rails

I'm trying to build a JobReport model that holds the return value from GoodJob jobs. The two fields I could build an association on, id and active_job_id, are problematic. The id field is set to return the active_job_id in the Job class:
# from good_job-3.12.1/app/models/good_job/job.rb
def id
active_job_id
end
The good_jobs.active_job_id field has no uniqueness constraint, and setting it as a foreign key throws a postgres error.
How can I link these two tables?
Here's the migration I'm using to create the job_reports table:
class CreateJobReports < ActiveRecord::Migration[7.0]
def change
create_table :job_reports do |t|
t.text :report
t.uuid :good_job_id
t.timestamps
end
add_foreign_key :job_reports, :good_jobs, column: :good_job_id, primary_key: :id
end
end
My JobReport model:
class JobReport < ApplicationRecord
belongs_to :good_job, class_name: 'GoodJob::Job', foreign_key: 'id'
end
And my good_job.rb initializer contains:
GoodJob::Job.class_eval do
has_one :job_report, dependent: :destroy
end
When I create a JobReport, tie it to a Job, and save it, postgres complains that the id doesn't exist in good_jobs, because it's trying to use the active_job_id:
irb(main):001:0> jr = JobReport.new; gj = GoodJob::Job.last
=>
#<GoodJob::Job:0x00007ff6950cda30
...
irb(main):002:0> jr.good_job = gj
=>
#<GoodJob::Job:0x00007ff6950cda30
...
irb(main):003:0> jr.save
/usr/local/bundle/gems/activerecord-7.0.4.1/lib/active_record/connection_adapters/postgresql_adapter.rb:768:in `exec_params': PG::ForeignKeyViolation: ERROR: insert or update on table "job_reports" violates foreign key constraint "fk_rails_6135bfd69e" (ActiveRecord::InvalidForeignKey)
DETAIL: Key (good_job_id)=(fdc02e75-a06a-4727-b790-9a846f61ed7d) is not present in table "good_jobs".
/usr/local/bundle/gems/activerecord-7.0.4.1/lib/active_record/connection_adapters/postgresql_adapter.rb:768:in `exec_params': ERROR: insert or update on table "job_reports" violates foreign key constraint "fk_rails_6135bfd69e" (PG::ForeignKeyViolation)
DETAIL: Key (good_job_id)=(fdc02e75-a06a-4727-b790-9a846f61ed7d) is not present in table "good_jobs".
irb(main):004:0> gj.id
=> "fdc02e75-a06a-4727-b790-9a846f61ed7d"
irb(main):005:0> gj.active_job_id
=> "fdc02e75-a06a-4727-b790-9a846f61ed7d"
irb(main):006:0> gj.attributes["id"]
=> "edc27b66-975d-4017-a09f-2d0cec332a0c"
As I mentioned before, if I give up on the ID column and switch to the active_job_id column, postgres says I can't use it as a foreign key b/c there's no uniqueness constraint. Sure, I could edit the GoodJob tables, but I'd prefer to use the drop-in form of the gem without tampering with it for upgrades and whatnot down the road.
Edit: I implemented Max's suggestion, but it's still trying to use the active_job_id column of the good_jobs table instead of the id column.
class JobReport < ApplicationRecord
belongs_to :good_job, class_name: 'GoodJob::Job', foreign_key: 'good_job_id', primary_key: 'id'
end
irb(main):010:0> jr = JobReport.new; gj = GoodJob::Job.last
=>
#<GoodJob::Job:0x00007f70ec430918
...
irb(main):011:0> jr.good_job = gj
=>
#<GoodJob::Job:0x00007f70ec430918
...
irb(main):012:0> jr.save
/usr/local/bundle/gems/activerecord-7.0.4.1/lib/active_record/connection_adapters/postgresql_adapter.rb:768:in `exec_params': PG::ForeignKeyViolation: ERROR: insert or update on table "job_reports" violates foreign key constraint "fk_rails_6135bfd69e" (ActiveRecord::InvalidForeignKey)
DETAIL: Key (good_job_id)=(fdc02e75-a06a-4727-b790-9a846f61ed7d) is not present in table "good_jobs".
/usr/local/bundle/gems/activerecord-7.0.4.1/lib/active_record/connection_adapters/postgresql_adapter.rb:768:in `exec_params': ERROR: insert or update on table "job_reports" violates foreign key constraint "fk_rails_6135bfd69e" (PG::ForeignKeyViolation)
DETAIL: Key (good_job_id)=(fdc02e75-a06a-4727-b790-9a846f61ed7d) is not present in table "good_jobs".
irb(main):013:0> gj.id
=> "fdc02e75-a06a-4727-b790-9a846f61ed7d"
irb(main):014:0> gj.active_job_id
=> "fdc02e75-a06a-4727-b790-9a846f61ed7d"
irb(main):015:0> gj.attributes['id']
=> "edc27b66-975d-4017-a09f-2d0cec332a0c"
Here's the schema of the two tables:
development=# \d good_jobs
Table "public.good_jobs"
Column | Type | Collation | Nullable | Default
---------------------+--------------------------------+-----------+----------+-------------------
id | uuid | | not null | gen_random_uuid()
queue_name | text | | |
priority | integer | | |
serialized_params | jsonb | | |
scheduled_at | timestamp(6) without time zone | | |
performed_at | timestamp(6) without time zone | | |
finished_at | timestamp(6) without time zone | | |
error | text | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
active_job_id | uuid | | |
concurrency_key | text | | |
cron_key | text | | |
retried_good_job_id | uuid | | |
cron_at | timestamp(6) without time zone | | |
batch_id | uuid | | |
batch_callback_id | uuid | | |
Indexes:
"good_jobs_pkey" PRIMARY KEY, btree (id)
"index_good_jobs_on_cron_key_and_cron_at" UNIQUE, btree (cron_key, cron_at)
"index_good_jobs_jobs_on_finished_at" btree (finished_at) WHERE retried_good_job_id IS NULL AND finished_at IS NOT NULL
"index_good_jobs_jobs_on_priority_created_at_when_unfinished" btree (priority DESC NULLS LAST, created_at) WHERE finished_at IS NULL
"index_good_jobs_on_active_job_id" btree (active_job_id)
"index_good_jobs_on_active_job_id_and_created_at" btree (active_job_id, created_at)
"index_good_jobs_on_batch_callback_id" btree (batch_callback_id) WHERE batch_callback_id IS NOT NULL
"index_good_jobs_on_batch_id" btree (batch_id) WHERE batch_id IS NOT NULL
"index_good_jobs_on_concurrency_key_when_unfinished" btree (concurrency_key) WHERE finished_at IS NULL
"index_good_jobs_on_cron_key_and_created_at" btree (cron_key, created_at)
"index_good_jobs_on_queue_name_and_scheduled_at" btree (queue_name, scheduled_at) WHERE finished_at IS NULL
"index_good_jobs_on_scheduled_at" btree (scheduled_at) WHERE finished_at IS NULL
Referenced by:
TABLE "job_reports" CONSTRAINT "fk_rails_6135bfd69e" FOREIGN KEY (good_job_id) REFERENCES good_jobs(id)
development=# \d job_reports
Table "public.job_reports"
Column | Type | Collation | Nullable | Default
-------------+--------------------------------+-----------+----------+-----------------------------------------
id | bigint | | not null | nextval('job_reports_id_seq'::regclass)
report | text | | |
good_job_id | uuid | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
Indexes:
"job_reports_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fk_rails_6135bfd69e" FOREIGN KEY (good_job_id) REFERENCES good_jobs(id)

Related

How to generate model with the table named "document" with the column of id:int, file:blob, status:enum, exp_date:datetime?

I am using Rails 6 with ActiveStorage.
I have a table of User, Signee, Signature, Signature_template, Position and Document.
I have a table also named "signature_template" with the column of user_id:int, file:blob(this is my question with document), created_at:datetime and updated_at:datetime.
You can create from your terminal
rails generate model Document
And then add migration file as follow
create_table :documents do |t|
t.binary :file
t.integer :status, default: 0
t.datetime :exp_date
end
Rails will automatically create id so you don't need to create it
For binary type it depend with your your database system, here is mapping.
For some famous database system
Database System Mysql will be created as blob,
Postgres -> bytea
SQLite -> blob
Oracle -> blob
For enum you can create as integer, but then in your Document model
you should provide information as follow (change the status to follow your needs)
class Document < ApplicationRecord
enum status: %i(draft verified published)
...
end
And here is some detail for column type in rails for your documentation type
+-------------+---------------------------------------------------------------+
| column type | Description |
+-------------+---------------------------------------------------------------+
| :string | Limited to 255 characters by default, Might be case-sensitive |
| :text | Generally unlimited length depending on database |
| :integer | Whole number, in contrast to :decimal or :float. |
| :decimal | Stored with specified precision. Use for math accuracy. |
| :float | Floating-point decimal number with fixed precision |
| :boolean | True or false. |
| :binary | Raw chunks of data saved in database-specific way. |
| :date | Year, month and day (no time). |
| :time | Hours, minutes, seconds (no date). |
| :datetime | Date and time stored together. |
| :timestamp | Exactly the same as :datetime on Rails. |
+-------------+---------------------------------------------------------------+

Rails and postgresql not dialoguing properly on record creation

The following record creation is failing as the data is being misinterpreted by Postgresql
SQL (1.3ms) INSERT INTO "gruppomerceologicos" ("nome", "reparto_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["nome", "Salumeria"], ["reparto_id", 1], ["created_at", "2020-03-13 09:24:15.529470"], ["updated_at", "2020-03-13 09:24:15.529470"]]
(0.2ms) ROLLBACK
Completed 500 Internal Server Error in 11ms (ActiveRecord: 2.5ms)
PG::NotNullViolation - ERROR: null value in column "id" violates not-null constraint
DETAIL: Failing row contains (null, Salumeria, 2020-03-13 09:24:15.52947, 2020-03-13 09:24:15.52947, 1).
The row creation is in effect incorrect as PostgreSQL is attempting to handle a record with a nullvalue for id
The controller is standard
#gruppomerceologico = Gruppomerceologico.new(gruppomerceologico_params)
authorize #gruppomerceologico
#gruppomerceologico.save
respond_with(#gruppomerceologico)
The rails debugger does however show that it is attempting to handle a record without an ID at point where error is occuring
#gruppomerceologico
#<Gruppomerceologico id: nil, nome: "Salumeria", created_at: "2020-03-13 08:24:15", updated_at: "2020-03-13 08:24:15", reparto_id: 1>
Postgre has the following data structure
Table "public.gruppomerceologicos"
Column | Type | Modifiers
------------+-----------------------------+-----------
id | integer | not null
nome | character varying |
created_at | timestamp without time zone | not null
updated_at | timestamp without time zone | not null
reparto_id | integer |
Indexes:
"gruppomerceologicos_pkey" PRIMARY KEY, btree (id)
"index_gruppomerceologicos_on_reparto_id" btree (reparto_id) Foreign-key constraints:
"fk_rails_d8a087054a" FOREIGN KEY (reparto_id) REFERENCES repartos(id) Referenced by:
TABLE "materiagrezzas" CONSTRAINT "fk_rails_2b6ca167a0" FOREIGN KEY (gruppomerceologico_id) REFERENCES gruppomerceologicos(id)
Normally Postgre knows it is creating a new record and will thus assign the sequential ID and create the record. But this is not happening. Where is the gap between rails and Postgresql?
The initial migration:
class CreateGruppomerceologicos < ActiveRecord::Migration
def change
create_table :gruppomerceologicos do |t|
t.string :nome
t.timestamps null: false
end
end
end
followed by an update in migration
class AddRepartoToGruppomerceologicos < ActiveRecord::Migration
def change
add_reference :gruppomerceologicos, :reparto, index: true, foreign_key: true
end
end
Update
Double checking with a development server, the \d query for the table returns something completely different:
Table "public.gruppomerceologicos"
Column | Type | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+-------------------------------------------------
id | integer | | not null | nextval('gruppomerceologicos_id_seq'::regclass)
nome | character varying | | |
created_at | timestamp without time zone | | not null |
updated_at | timestamp without time zone | | not null |
where the nextval is obviously in proper play. The two databases were somehow out of synch. As suggested below by #sebastianPalma select nextval('gruppomerceologicos_id_seq'::regclass); will directly reveal the discrepancy.

Rails 3.2.12 uninitialized constant error

I've been beating my head against my desk all afternoon trying to get past an uninitialized constant error, but can't seem to get beyond it. I have the following models:
sub_award.rb
class SubAward < ActiveRecord::Base
has_many :sub_awards_colleges, foreign_key: [:award_id, :sub_id]
has_many :colleges, through: :sub_awards_colleges
end
sub_awards_colleges.rb
class SubAwardsCollege < ActiveRecord::Base
belongs_to :sub_award, foreign_key: [:award_id, :sub_id]
belongs_to :college
end
colleges.rb
class College < ActiveRecord::Base
has_many :sub_awards_colleges, foreign_key: [:award_id, :sub_id]
has_many :sub_awards, through: :sub_awards_colleges
end
When I attempt to call sub_award.colleges from my view I get the error:
ActionView::Template::Error (uninitialized constant SubAward::SubAwardsCollege)
I believe I have followed all the proper rails conventions and I have other associations within the sub_award model that I set up the same and are working fine. The tables look like (unrelated attribute omitted):
mysql> DESCRIBE sub_awards_colleges;
+------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| award_id | int(11) | NO | | NULL | |
| sub_id | int(11) | NO | | NULL | |
| college_id | int(11) | NO | MUL | NULL | |
+------------+---------+------+-----+---------+----------------+
mysql> DESCRIBE sub_awards;
+--------------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------------+--------------+------+-----+---------+-------+
| award_id | int(11) | NO | | NULL | |
| sub_id | int(11) | NO | | NULL | |
mysql> DESCRIBE colleges;
+-------------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
Any help is appreciated and please let me know if you'd like me to provide more information. Thanks!
You might have missed to place your sub_awards_colleges.rb file in app/models !
I had a similar problem once.
Also why are award_id ans sub_id columns in sub_awards_colleges, As far as I guess, you have a mapping table which should map it to colleges. and there is no use of college_id in mapping, all the mapping is based of award_id and sub_id. This is pretty confusing. You might want to have a look at your mappings once again.
I solved my issue, but I'm still not entirely sure what the problem was. To solve I blew away the existing sub_awards_college.rb file. I then created a new migration to drop the sub_awards_colleges table and re-create it and generated a fresh sub_awards_college.rb model. I re-populated the sub_awards_college.rb file with my associations:
belongs_to :sub_award, foreign_key: [:award_id, :sub_id]
belongs_to :college, class_name: "College"
adding the class_name option (although it shouldn't be necessary according to conventions) and also added additional parameters to the sub_award.rb associations so it read as:
has_many :sub_awards_colleges, foreign_key: [:award_id, :sub_id], source: :sub_awards_colleges, class_name: "SubAwardsCollege"
has_many :colleges, through: :sub_awards_colleges, after_add: :invalidate_matching, after_remove: :invalidate_matching, source: :college, class_name: "College"
again, shouldn't be necessary according to conventions, but restarted my app and it's now working. So not exactly sure what my problem was, maybe there was something hokey in the database regarding how my tables were set up so they weren't aligning properly with the models, but it's working now and thank you for taking the time to review and provide comments.

Ruby on Rails: How can i get the username from users table with Post.paginate?

In index action of PostController
#posts = Post.paginate(:per_page => 15, :page => params[:page], :order => 'created_at DESC')
posts table
Table "public.posts"
Column | Type | Modifiers
-------------+------------------------+----------------------------------------------------
id | integer | not null default nextval('posts_id_seq'::regclass)
title | character varying(100) | not null
content | character varying(500) | not null
created_at | date |
updated_at | date |
tags | character varying(55) | not null default '50'::character varying
category_id | integer | not null default 1
user_id | integer |
Indexes:
"posts_pkey" PRIMARY KEY, btree (id)
users table
Table "public.users"
Column | Type | Modifiers
------------------------+-----------------------------+----------------------------------------------------
id | integer | not null default nextval('users_id_seq'::regclass)
email | character varying(255) | not null default ''::character varying
encrypted_password | character varying(128) | not null default ''::character varying
reset_password_token | character varying(255) |
reset_password_sent_at | timestamp without time zone |
remember_created_at | timestamp without time zone |
sign_in_count | integer | default 0
current_sign_in_at | timestamp without time zone |
last_sign_in_at | timestamp without time zone |
current_sign_in_ip | character varying(255) |
last_sign_in_ip | character varying(255) |
confirmation_token | character varying(255) |
confirmed_at | timestamp without time zone |
confirmation_sent_at | timestamp without time zone |
username | character varying(255) | not null
is_admin | boolean | default false
created_at | timestamp without time zone |
updated_at | timestamp without time zone |
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"index_users_on_confirmation_token" UNIQUE, btree (confirmation_token)
"index_users_on_email" UNIQUE, btree (email)
"index_users_on_reset_password_token" UNIQUE, btree (reset_password_token)
"index_users_on_username" UNIQUE, btree (username)
I need to get username from users table(need joining), not the user_id from posts table. I have used pagination in index.html.erb
Getting the username how can i display the username of a post in index.html.erb
just simple include should do the trick:
#posts = Post.paginate(:per_page => 15, :page => params[:page], :order => 'created_at DESC', :include => :user)
and then in the view:
post.user.username

Is there an activerecord relationship to solve this problem?

I can't seem to wrap my head around this. I have three tables:
mysql> desc users;
+----------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(255) | YES | | NULL | |
+----------------------+--------------+------+-----+---------+----------------+
mysql> desc mentions;
+------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| message_id | int(11) | YES | | NULL | |
| mentionable_type | varchar(255) | YES | | NULL | |
| mentionable_id | int(11) | YES | | NULL | |
+------------------+--------------+------+-----+---------+----------------+
mysql> desc messages;
+------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| body | text | YES | | NULL | |
| user_id | int(11) | YES | | NULL | |
+------------+----------+------+-----+---------+----------------+
And the following relationships:
class User < ActiveRecord::Base
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
has_many :mentions
end
class Mention < ActiveRecord::Base
belongs_to :mentionable, :polymorphic => true
belongs_to :message
end
I'm not sure if I'm using it correctly, but I used the polymorphic relationship in Mention because mentionable_type could be 'User' or 'Group'. I've left the Group stuff out of this post as it's not related to this question.
When a user creates a Message, their user_id is stored in the messages table. I can easily return a list of a user's "authored" messages with:
current_user.messages
Similar to a tweet, the message's body may, or may not, contain mentions of n users or groups. When the message "I'm having lunch with #userA, #userB, and #groupX." is created, the body would be parsed and those three "mentions" would be created as well.
I can easily return all of a user's "mentions" with:
current_user.mentions
If I want to see the message of a mention, I can do:
mention = current_user.mentions.first
mention.message
What I can't seeem to figure out is a clean way to combine the two and get a list of messages that a user created AND were mentioned in. Any ideas?
I your User model, this line should be present for polymorphic relationships.
class User
has_many :messages
has_many :mentions, :as => :mentionable
end
And try this:
user_id = 10
#messages = Message.find(:all, :joins => [:mentions],
:conditions => ['messages.user_id = ?', user_id])

Resources