Rails/postgres, 'foreign keys' stored in array to create 1-many association
You won't be able to make Rails aware of this array and use it for associations.
But if you want quicker searching / filtering of Tasks assigned to users you can keep an array of User IDs in the Task object. Otherwise, you'd have to do a JOIN to find all tasks assigned to Alice, in your standard association table.
So the solution is to keep the association table but also duplicate the assignee User ID into the Task object and use that ID list for faster searching / filtering.
You'll need to hook into the after_create
and after_destroy
lifecycle for the assignee objects and insert new Assignee IDs into the Task record array. And then when an asignee is removed from a task update the array to remove the ID.
See Postgres docs for all the Array operators:
Something like this:
class Task < ActiveRecord::Base
has_many :assignees, :dependent => :destroy
end
class Asignee < ActiveRecord::Base
belongs_to :task
after_create :insert_task_assignee
after_destroy :remove_task_assignee
# assumes that there is a column called assignee_id
# that contains the User ID of the assigned person
private
def insert_task_assignee
# TODO: check for duplicates here - before we naively push it on?
task.assignee_list = task.assignee_list.push(assignee_id)
task.assignee_list.save
end
def remove_task_assignee
id_list = task.assignee_list
id_list.reject! { |candidate_id| candidate_id == assignee_id }
task.assignee_list = id_list
task.assignee_list.save
end
end
# find all tasks that have been assigned Alice & Bob
# this will require the `postgres_ext` gem for Ruby / Postgres array searching
tasks = Task.where.contains(:assignee_list => [alice.id, bob.id]).all
Rails One-To-Many associations: Defining a foreign key field when scaffolding?
Its not necessary to have a foreign_key
in article
table but you should have. Code to generate the scaffold should be like below
rails g scaffold article vendor:references field1:datatype field2:datatype ...
It will generate a rails migration with the create
statement for article
table.It will have a foreign_key i.e.
vendor_id
like belowclass CreateArticles < ActiveRecord::Migration[5.1]
def change
create_table :articles do |t|
t.references :vendor, foreign_key: true
.....other fields
t.timestamps
end
end
end
If you dont want forign_key
, you can remove foreign_key
from the migrationbelongs_to :vendor
will be automatically added in the article
model.
Store multiple foreign key in one attribute
This is the wrong approach. Don't store the foreign keys in an array. Make a join table instead.
In the simplest arrangement, use a has-and-belongs-to-many relationship. This is for when you don't need to refer to the thing that connects the two models as a thing in its own right e.g. a car has many parts. If the connection is a real thing e.g. appointments connecting doctors and patients, then use a has-many-through relationship, where the join table has its own model.
The reason these approaches are better is that SQL will not be able to use proper JOINs to query across your data if you store the foreign keys in an array. Also, you are unable to specifically mark them as foreign keys, so the database will not be able to enforce referential integrity for you should you need this.
Rails method about foreign keys
Using ActiveRecord::reflect_on_all_associations and Array#reject you can filter all has_many
association without :dependent
key
Company.
reflect_on_all_associations(:has_many).
reject { |association| association.options[:dependent] }
To get table names you can use plural_name
attributeCompany.
reflect_on_all_associations(:has_many).
reject { |association| association.options[:dependent] }.
map(&:plural_name)
How to store foreign keys in the parent with a has_many relationship
The final answer, using Rails 4's support for PostgresQL's array column type for questions_list
, was:
class Product < ActiveRecord::Base
has_many :questions, :after_add => :add_to_questions_list, :after_remove => :remove_from_questions_list
def add_to_questions_list(question)
update_attribute :questions_list, questions_list << question.id
end
def remove_from_questions_list(question)
update_attribute :questions_list, questions_list.reject{|i| i == question.id}
end
def versioned_questions
questions_list.map{|id| Question.find(id)}
end
end
Setting up Foreign Keys in Rails with Postgres
liker.likes.where(likee: liker)
returns only a relation. You have to execute your query like this liker.likes.where(likee: liker).exists?
has_many :likes
within your User
model expects a column user_id
within your table likes
. With the code given I would think you have to change it to
class User < ApplicationRecord
has_many :other_likes, foreign_key: :liker_id
has_many :likes, foreign_key: :likee_id
end
belongs_to :connection
is a bad name in my point of view. ActiveRecord::Base
has a class method with the same name. Maybe you will run into issues with such an association name some day.
Related Topics
Indent Multiline String in Erb
Ruby: Start Reading at Arbitrary Point in Large File
Getting a "Connection Reset by Peer" Error When Hitting Google Contacts API
Ruby: Converting a Nested Ruby Hash to an Un-Nested One
Get Route for Base Class of Sti Class in Rails
How to Get All Message History from Hipchat for a Room via The API
No Such File to Load - Mechanize
How to (Massively) Reduce The Number of SQL Queries in Rails App
How to Check If an Object Is Nil in a View in Ruby
What Is Ruby's Stringio Class Really
Question on Ruby Collect Method